Security is a key factor in developing many enterprise applications. Indeed, if you handle sensitive data or offer restricted services, you need a clearly defined security policy for your application and a framework that enforces it; you cannot let individual programmers guarantee that their code is safe. Server platforms such as J2EE (Java 2 Platform, Enterprise Edition) normally include some infrastructure to provide declarative security for your application components, usually via a role-based access-control system. Such systems do effectively move application security issues away from the programmer.
However, in some situations a security policy may define application-specific business rules beyond the declarative security model's scope. For an EJB-based (Enterprise JavaBeans) application, usually your only alternative is to place security code directly in the EJB business methods -- undesirably mixing business and security logic. The open source, J2EE-compliant JBoss application server offers a convenient proxy-based architecture for factoring out such custom security code into separate, configurable objects. In this article, I outline pressing reasons why you should use custom security, why you should keep it out of your business code, and how you can employ JBoss security proxies to secure your applications.
Note: You can download the source code examples for this article from the Resources section below.
Authorization mechanisms in an EJB server
As any article or book on the subject will tell you, a key area of application security is user-activity authorization: how do you control access to the components and services that your application offers? Once the system authenticates a user (typically by logging in with a name and password), it must check his every subsequent action to ensure that he has the necessary permissions. Most systems control access using a role-based mechanism -- they define a number of roles for the application, assign each user a subset of these roles, and specify the permissions in terms of the roles.
With such a mechanism, you delegate to the infrastructure -- in our case, a J2EE server -- the responsibility for applying the controls. The J2EE specification defines how to set up an application's roles and how to apply access controls to its services. To apply this mechanism to EJBs, you specify which roles can call which methods on a particular interface. When an EJB method is invoked on a user's behalf, then, before the invocation takes place on the bean implementation, the container checks whether the user has one of the roles required to call the method. If he doesn't, it raises a security exception and the method isn't called.
This all takes place outside the application code. The application's XML configuration files specify the security permissions, while the application server's security infrastructure defines the usernames and their associated roles. Scott Stark's "Integrate Security Infrastructures with JBossSX" (JavaWorld, August 2001) explains how to set up a typical J2EE application's security with JBossSX, and introduces the JAAS (Java Authentication and Authorization Service) architecture that JBoss uses to implement its security services. (I'd also like to thank Scott for providing useful feedback on this current article.)
So why keep security features out of your code? First, a bean coder may not be aware of how an individual bean will be used, so it makes sense to wait until you've assembled the beans to set the security constraints. Mainly, however, it's impractical and unreliable to put security code in every method you implement. Moreover, it's a nightmare to maintain such code if you decide to change the permissions for particular roles, add new roles, and so on, as these would be referenced throughout the code. So while it is possible in an EJB to obtain role information for the user calling the method, you should generally favor letting the container do the work.
Limitations of security-unaware applications
Forcing the container to enforce security policies frees the developer to code the application's business logic without worrying about security. It also allows the security policy and business logic to evolve separately.
However, those two are not necessarily completely independent. What if you want to apply security policies that depend on the application's business logic? Cases exist where simple role-based access control proves inadequate: there are no standard means to define policies that relate to specific bean instances, business data, or users. Example situations include:
- A customer can access only her own account
- A trader can only execute transactions that have a value less than one million Swiss francs
- A tax inspector is prohibited from modifying her own tax liability data
- An underage subscriber does not have access to an X-rated online movie
Each case above requires that you know exactly what the application is up to, or how the identity of the user making the call relates to the invoked business object. For example, is the user who is making the call the same person as the account owner?
While encouraging declarative security, most security architectures recognize that applications may, at some point, need to implement security policies in code. The CORBA security specification, for example, defines two separate security levels: level 1 for security-unaware applications and level 2 for security-aware applications. For EJB security, however, security awareness is limited to the methods
isCallerInRole(String roleName), made available to the bean developer by the
getCallerPrincipal() returns a
java.security.Principal instance, which represents the entity making the call (in our case, this will be a user who has logged into the system).
isCallerInRole(String roleName) checks whether the entity making the call has been allocated the given role, thus allowing access-control decisions to be made in the code.
Security proxies in JBoss
If you have to apply security policies dependent on your application's business logic, aren't you back to putting code in each bean method? The answer is generally yes. And, as I mentioned before, such a solution proves both fragile and unmanageable. As a solution, JBoss provides a neat alternative to embedding security code directly in your bean implementations: it decouples the security code from the bean code by letting you place the former in a separate, configurable proxy object. In addition to the normal security checks on the caller, JBoss adds an extra security interceptor to check with the bean's security proxy (if one is allocated) whether a particular method invocation should be allowed. You can apply the security checks to the bean as a whole or to individual methods.
Security policy is now enforced outside the bean, and you can implement each defined policy independently of the bean's business code. The JBoss solution separates developer roles, as the security code lives in separate components; in this way, a developer can implement a policy such as "only the account owner can invoke the account bean" by making a simple check of the caller principal against the bean's ID in the proxy. Thereafter, no amount of messing with the bean's code will break the security check.
So what does this buy us?
Security is usually considered a means of protection against malicious attacks -- to defend the system against deliberate attempts by a determined adversary to break into the system and abuse its services. Customized security policies like those described above will help stop such attacks. However, other potential threats may devastate an organization even more, and customized security can also prevent them. One such threat (albeit a mundane one) is the perennial problem of buggy code: it may require the remaining lifetime of the universe to break your transport mechanism's encryption algorithm, but that's no comfort if the wrong person accesses your sensitive data because of some unforeseen error in the code.
As a true example, I sometimes access my phone bill via my telephone company's Website. The site stores my personal contact information, and optionally stores more important details such as my credit card number. Not long ago, I logged in and was greeted with the cheery message, "Welcome back, Fred!" Very nice and friendly, except my name is not Fred. I tried again. "Welcome back, John!" Several more tries produced a different name each time. Further investigation revealed that the personal details were also not, in fact, my own. I called the site's technical support, who confessed that, yes, my details might have been supplied to someone else by accident.
My experience is a typical problem that has plagued many high-profile online applications, often resulting in bad publicity for the companies concerned. Online amateur traders have found themselves in charge of other people's portfolios, and online banking users have been supplied with someone else's account details. While the work of a malicious hacker can potentially be swept under the carpet, normal users will complain bitterly about these kinds of problems, and a flagship product can rapidly turn into a public relations disaster.
You'll find interesting examples of such tales of woe listed in Resources.
Create a JBoss security proxy
Before moving to the example code, let's first look at how to create security proxies in JBoss. A JBoss security proxy can take two forms:
- A class instance that implements the
org.jboss.security.Proxyinterface with two key methods,
invoke(). These are called prior to invoking the bean itself.
- An object that implements one or more methods from the bean's home or remote interface. Before calling the method on the bean, the bean container invokes the corresponding method on the proxy.
With the first approach, you merely create a class that implements the required interface, which is an untyped invocation interface particularly convenient for implementing all-or-nothing checks such as those based on the bean ownership. You can use the bean instance, passed as an argument to
invoke(), to apply checks that are dependent on the bean state. This option is best if you want to integrate an external security framework or to delegate the decision to a separate access-control service.
Use the second option if you don't want your proxy to have any JBoss code dependencies. The second approach may also be more acceptable if you want your developers to avoid reflection-style APIs; it follows a more natural proxy pattern where the proxy object looks like the business bean.
With the second approach, the container uses a wrapper instance of
org.jboss.security.Proxy to delegate calls to your proxy class. By default, the class
org.jboss.security.SubjectSecurityProxy is used, but it serves just as an example implementation; in practice, you should supply your own wrapper class. You can use the
org.jboss.security.AbstractSecurityProxy class as a base. You also have to provide an
org.jboss.security.SecurityProxyFactory instance and configure JBoss to use it to create instances of your proxy on demand. To do so, you supply the
SecurityProxyFactoryClassName attribute when setting up the
JAASSecurityManager in JBoss's main configuration file,
<!-- JAAS security manager and realm mapping --> <mbean code="org.jboss.security.plugins.JaasSecurityManagerService" name="Security:name=JaasSecurityManager"> <attribute name="SecurityManagerClassName">org.jboss. security.plugins.JaasSecurityManager</attribute> <attribute name="SecurityProxyFactoryClassName">org.jboss.security. SubjectSecurityProxy</attribute> </mbean>
You then write code in your proxy for each method you wish to protect. In contrast with the first approach, here you don't automatically receive an instance of the invoked bean. To get around this,
AbstractSecurityProxy searches for the
setBean() method on your delegate class; so, if you must access the bean state, you can implement that method. You will receive a reference to the bean that you can cache similarly to the
AbstractSecurityProxy's current implementation doesn't complain if the invoked method has no matching method on the proxy. Therefore, if someone adds a new method to the bean or changes an existing bean's signature, such actions could silently bypass your access controls. Indeed, the checks will not be applied unless you also change the proxy accordingly. If you wish to enforce strict checking, configure your proxy wrapper class to throw an exception if no matching method is found on the proxy.
Use the proxy
To get JBoss to use your proxy, you specify the class name in the
security-proxy element of the
jboss.xml configuration file (which contains vendor-specific configuration information and accompanies the standard
ejb-jar.xml file for your application). We'll use the classes from the example code as an illustration. If you wish to protect the entity bean
TradingAccount, you would code: