Security is something that no application can afford to ignore, and it is a particularly large challenge for web applications. A web application deployed on the Internet is subject to potential attack from anywhere, whereas an application contained on an internal intranet has at least fewer locations from which it is vulnerable to attack (usually).
The HTTP protocol, which most web applications rely on, was not intended as a secure or session-oriented protocol at all. As a result, web applications must make use of a number of techniques to layer security onto this protocol. Those of who are not in the business of wheel-reinventing will immediately look about for a framework, preferably based on existing standards, to provide these services for us. Sometimes, however, no one framework is a perfect fit for the job at hand.
We might want some of the features of one framework, but other features that are only available in another. In this article we´ll use as a backdrop for our discussion an open source project designed for just such a purpose, the Keel meta-framework. Its security design is a practical example of a security structure that can be applied to many different projects.
The first step in figuring out what someone is allowed to do is to figure out who they are, in a provable manner. Java provides the Java Authentication and Authorization Service (JAAS) API for just this purpose. By selecting and configuring the proper LoginModule (or modules, as more than one can be used) we can easily take advantage of a substantial number of different authentication mechanisms, including LDAP, Microsoft´s Active Directory (which is close but not quite LDAP-compliant), file-based and even database-based authentication. Open source LoginModule implementations exist for all of these options, as well as for many more. Multiple LoginModules can even be used to request the user authenticate themselves by more than one means – perhaps a passphrase and a biometric signature, for example.
Ideally, we will use a existing component to provide the front-end for JAAS´s LoginModules (which are UI-independent), enabling us to verify the identity of our users with little or no actual coding required. Many frameworks also provide all of the trimmings needed for a complete authentication solution, including a way for a user to request a new password, verify their email address, and other similar operations.
Okay, now we´ve established, with some degree of certainty (which we´ll revisit in a moment), who this user is. In a web application, this does us little good unless we have a way to associate this identity with subsequent requests. Otherwise, the user would have to re-establish their identity with every page request made, and for some reason users seem to find this quite tedious!
The answer, of course, is sessions, which the servlet API keeps track of for us quite handily. In a stateless protocol, there must be some identifier passed with each request so that a session can be associated with it. This can be done via either a temporary cookie, or if cookies are turned off or not available, by ´encoding´ each request – including the session identifier as a parameter to each request. In a distributed environment, however, this session must be synchronized with a per-user context on all available application servers. For example, a user logs in and makes a request of the application. It so happens that server “a” handles this request, and records some information about the state of the user´s progress in the application. Some web application servers, such as the latest versions of Tomcat, provide a way for multiple instances of the web server to automatically synchronize session information between each other. This handles the web-application layer – but if we are using a multi-tiered application server, we´re not out of the woods yet. If the next request the user makes is handled by application server “b”, the context may not be available. Keel provides a simple mechanism for ensuring context sharing between application servers, whether on the same machine or distributed in a cluster, allowing sessions to be maintained not only with the web server, but with distributed application servers as well.
As most web-developers know, http can be combined with the secure sockets layer protocol (SSL) to encrypt communications between the browser and the server, as well as providing verification of the identity of the server via the use of a certificate. Developers who have set up servers such as Tomcat are familiar with the process of creating or installing the necessary server-side certificate. (As an aside, the recent introduction in some environments of “SSL filtering”, intended for virus and other content-checking, significantly reduces the level of security provided by SSL – it is something to be cautious of). So, SSL can give us a certain amount of assurance that the communication channel is not intercepted, and provides some verification to the user that the site they are talking to is in fact they site it claims to be. Of course, a site using a self-signed certificate is essentially only asserting on their own authority that they are who they claim to be – a certificate signed by a third party (several companies provide this service) adds an additional level of assurance.
Fewer developers, however, have tackled the issue of client-side certificates. These certificates, installed in the client´s browser, can provide the same kind of confirmation to the server as the server-side certificates do for the client. They provide an additional confirmation that the client is in fact who they say they are. One issue with https that can arise is that a secure session is distinct from a non-secure session with the same client. This means that using, for instance, a secure https connection for logging in doesn´t necessarily mean that the login data will be available to the remaining application. Again, this is an area where your framework should take care of the details, synchronizing secure session data and regular session data, and making sure that requests designated as requiring secure connection are always handled via https. Client-side certificates can also be used to provide some assurance that both ends of a Web-Services communication are who they say they are – but that´s another article.
Part of the authentication process is to define the “roles” of the authenticated user. Roles fulfill the same purpose as what Unix old-timers (such as myself) call groups. They allow authorization to be granted or denied en-masse, to entire groups, rather than one user at a time.
Having established who we´re communicating with, verifying this identity, and protecting the communication from casual interception, we finally come to the point of determining what this user is allowed to do. This is authorization, and it is often applied at several different levels:
A single class or related group of classes providing a service to an application can be considered a component. The most straightforward type of authorization is to grant or deny access to entire components, based on group membership. An example would be saying all members of group “customers” have access to the “DisplayAccountBalance” component.
Once a user has access to a component, what can they do with it? This is handled by defining Operations, where the set of operations available depends on the component being secured. For example, a persistent object, stored in a database, the available operations might be create, update, delete and read.
Securing an entire component may be too coarse-grained for many applications. By implementing a simple interface, components can become InstanceSecurable. Each instance of the secured class can then have permission assigned independently. An example might be a database factory component – if the name of the database were used as the identifier, security could be assigned to allow only users belonging to the appropriate groups access to certain databases. Of course, this is a simple example.
There is one even more detailed level of authorization that is sometimes required. If the specific state of an object is one of the factors determining whether or not that object is allowed, then invokation security is required. This allows the state of the object to be queried, and compared to one or more rules. For example, if a user is allowed to access a component called ´Account´, but only if the account balance is over zero, a rule is set up to reflect this, and each access to the ´Account´ component is checked through this rule. This level of security is also applicable to “row-level” database authorization, where the actual contents of the data determine the security for that item – an example of this is a user only being allowed to update a record he or she created.
IOC and security
Keel employs a concept (inherited from the underlying Apache Avalon framework) called “inversion of control”. A popular design pattern for component architectures, IOC refers to the approach where the container “drives” the components, supplying them with all they need to operate, and leading them through their execution lifecycle. The container always calls the component, never the reverse. If a component has dependencies on another component, it requests an instance of the needed component from the container. The fact that the container is the ´single point of access´ makes it easier to implement a pervasive security structure such as Keel´s.
Interfaces and swappable implementations
Many projects involving web-applications are not a “clean start” – they must integrate in some fashion with existing applications, this often includes making use of an existing application security mechanism. Frequently this will include a ´single sign on´ feature, making use of authentication information such as login and password from the legacy application (or possibly from the underlying operating system itself). JAAS helps here, as often all that is required is writing a new LoginModule. Legacy-based authentication is then often combined with customized authorization – starting from the basis of the three tiers described above (component, instance and invokation). The question then becomes “how do I implement part of the security system as custom code, while still maintaining the benefits of using a standards-based framework. We could of course just copy the standard code and modify it to suit, but this throws away one of the benefits of an open source solution – that the entire developer community shoulders the burden of maintenance collectively. By creating an orphan version, we take this responsibility on alone. This is why Keel is called a meta framework – it is designed to connect multiple frameworks and components, allowing specific portions of functionality to be ´swapped out´ and replaced with custom code only where needed.
All of the security functionality we describe in this article are in daily use in web applications, and specific examples can be seen in the open-source Keel meta framework. We´ve taken a very fast tour at a high altitude over some of the major topics of securing a web application – as you can imagine, it´s a topic you can go on learning about for a long time, and one which is sure to continue to evolve in the future.