Since the first time a login page was added to a Web application, security has always been one of the key components critical to the success of applications on the Web. Historically, everything was coded by hand. Each Web application had a custom method of authenticating and then authorizing users. Developers also built in components for registration, administration, and any other function needed. Though quite a bit of overhead, this approach allowed great flexibility.
With the advent of JAAS, the Java Authentication and Authorization Service, applications gained a set of interfaces and a configuration they could leverage to standardize those tasks. Even with the addition of JAAS to the specification, J2EE still has a few problems to resolve before application developers can stop creating custom APIs. Choosing between using the J2EE standards or building a custom solution requires knowing the trade-offs of each and, of course, your application's requirements.
This article aims to provide all the information required to decide between custom or container security. I discuss the most common application security functions to provide the necessary background on security. Following that discussion is a detailed explanation of the J2EE security implementations provided by the specifications as well as the most common methods of implementing custom security. After you better understand each of the methods, you should have enough information to choose which method best suits your application's requirements.
What is a container?
Before we discuss the different security types and security implementation concerns, let's review what a container is. A container is an environment in which an application runs. It is also synonymous with a J2EE application server. In terms of J2EE containers, a J2EE application runs inside the container, which has specific responsibilities with respect to the application. There are many different types of J2EE containers and different levels of J2EE support. Tomcat from Apache is a Web container that implements only the Servlet (Web application) portions of the J2EE specification. BEA's WebLogic is a fully compliant J2EE application server, meaning it supports all aspects of the J2EE specification and has passed Sun's J2EE certification tests. If you are unsure of the support your application server provides, contact the vendor for more information.
Another topic we must cover before we begin is the distinction between application security and other types of security. Application security is security performed directly by an application or indirectly by a framework or container for an application with respect to that application's users. An example of an application user is someone who logs into an online bookstore and purchases a few Java books. Other types of security exist, such as network security and JVM security. One example of those security types is the user who starts a Java process on a machine. Throughout the rest of this paper, whenever I discuss security, I mean application security. The other types of security reach outside this discussion's scope.
The focus here is specifically J2EE security, which is a type of application security because it deals with a J2EE application's users (i.e., callers). A user might be someone using the online bookstore or another application that uses the bookstore application's purchasing services, such as another online reseller.
Security functions of applications
There are five main functions when considering application security: authentication, authorization, registration, account maintenance (updates), and account deletion/inactivation. Though only a small subset of all the possible functions an application might have, these are the most fundamental and fairly standard for all applications. Less formally, these functions are knowing the user (authentication), knowing what the user can do (authorization), creating new users (registration), updating user information (account maintenance), and removing a user or preventing a user from accessing the application (account deletion).
Most applications allow either the user or an administrator to execute these functions. When users execute these functions, they do so for themselves. Administrators always perform these functions on behalf of other users.
As will be illustrated, all of these functions cannot be accomplished without a custom solution, even for authentication. We will go over each one briefly to further illustrate the concepts and what J2EE lacks that must be custom built.
Authentication is the process of identifying a user interacting with an application. At the time of this writing, J2EE authentication could be implemented using a variety of solutions, each one defined as part of the J2EE specification (version 1.0-1.4). Authentication is the main concept of this discussion and will be covered in greater detail later. It is important to realize that authentication is the security function that has the most support within the J2EE specification, but custom code or configuration is usually required to implement J2EE authentication (aka container authentication).
Authorization is the process of verifying that a user has permission to take a specific action. J2EE covers this topic, but it is constrained to role-based authorization, which means that activity can be constrained based on the roles the user has been given. For example, users in the manager role might be able to delete inventory, while users in the employee role might not.
Additionally, applications might consider two different types of authorization: Java Runtime Environment (JRE)/container and application authorization. JRE/container authorization is the process of determining whether the user making the request has privileges to do so. The JRE/container determines this prior to any code executing. An example is a J2EE container that must first check whether the current user has permissions to execute a servlet (via a resource URL constraint) before executing the servlet. This type of authorization is also known as declarative security because it is declared in the configuration files for the Web application. Unless supported by the container, declarative security cannot be modified at runtime. Declarative security can be used in many ways to authorize J2EE application users, but that topic reaches outside this discussion's scope. (See the Servlet 2.3 Specification Chapter 12. Section 2 covers declarative security, and 8 is a good starting point for security constraints.)
As mentioned before, the user might be another application or simply an application user. Either way, JRE/container authorization is performed during each request. These requests might be HTTP requests from a browser to a Web application or remote EJB (Enterprise JavaBeans) calls. In either case, provided that the JRE/container knows the user, it can perform authorization based on that user's information.
Application authorization is the process of authorizing as the application executes. Application authorization can further be broken down into role-based and segment-based authorization. An example of role-based application authorization is when an application applies different levels of markup based on whether a user is an employee or a visitor (i.e., an employee discount). J2EE provides APIs called programmatic security to accomplish role-based authorization (see the Servlet 2.3 Specification Chapter 12, Section 3 for more information).
Segment-based authorization is authorization based on a user's other attributes, such as age or hobbies. Segment-based authorization is called such because it groups users into segments based on specific attributes. J2EE has no method of implementing segment-based authorization. An example of segment-based authorization is whether a button on a form is visible to users over 40 years of age. Certain vendors may offer this type of authorization, but this would guarantee vendor lock-in in all cases.
Registration is the process of adding a new user to the application. Application users might be able to create new accounts for themselves or the application might choose to constrain this activity to application administrators. The J2EE specification doesn't have an API or configuration that allows applications to add new users; therefore, this type of security is always custom built. J2EE lacks the ability to tell the container a new user has registered and that her information must be persisted and maintained during her session.
Account maintenance is the process of changing account information, such as contact information, logins, or passwords. Most applications allow application users, as well as administrators, to perform maintenance. The J2EE specification also lacks an API or configuration for account maintenance. A mechanism is missing for informing the container that user information has changed.
Account deletion is usually constrained to administrative users only. On rare occasions, some applications might allow users to delete their own accounts. Most applications in fact never delete users; they simply inactivate the account so the user can no longer log in. Doing hard and fast deletes is usually frowned upon because the account data is much more difficult to resurrect if need be. J2EE provides no way of removing or inactivating users from applications. It lacks a mechanism for telling the container that a specific user has been inactivated or removed. J2EE also lacks a mechanism for immediately logging a user out of the application when her account has been deleted.
What is container authentication?
Container authentication is the process of telling the container the identity of the user making the current request. For most containers, this process involves associating the current
ServletRequest object, the current execute thread, and an internal session with the user's identity. By associating a session with the identity, the container can guarantee that the current request and all subsequent requests by the same user can be associated with the same session, until that user's session expires. This session object is usually not the same as the
HttpSession object, although the former is used to create and maintain the latter. Each subsequent request by the same user is associated with the session using either URL rewriting or a session cookie, according to the Servlet 2.3 Specification, Chapter 7.
As mentioned above in our discussion of authorization, every action that the container takes as well as every action the JRE takes on that user's behalf are carefully checked to ensure the user has permission to execute the action. To reiterate our previous example, when the container executes a servlet on behalf of the user, it verifies that the user belongs to the set of roles given permissions to execute that servlet. JRE 1.4 also performs these checks for many actions, including when a file or socket opens. JRE authentication is a powerful concept and can ensure that every request to a container is essentially safe.
Currently, J2EE provides a few different mechanisms for implementing user authentication. These include form-based authentication, HTTPS client authentication, and HTTP basic authentication. JAAS is included as a required authentication method that containers must support. But the specification is not strict about how the container should provide this functionality; therefore, each container provides different support for JAAS. In addition, JAAS by itself is a standalone authentication framework and could be used to implement container authentication regardless of whether the specification supports it. I explain this concept in more detail later.
Each of the authentication mechanisms provides a standard way of giving the container information about the user. I refer to this as credential realization. The container still must use this information to verify that the user exists and has permissions sufficient to make the request. I refer to that as credential authentication. Some containers provide configuration to set up credential authentication and others provide interfaces that must be implemented.
J2EE authentication methods
Let's look briefly at some of the most common methods for implementing and configuring container authentication.
Form-based authentication allows users to be identified and authenticated with the J2EE application server using any HTML form. The form action must be
j_security_check and two HTTP request parameters (form input fields) must always be in the request, one called
j_username and the other,
j_password. Using form-based authentication, credential realization occurs when the form is submitted and the username and password are sent to the server.
Here is an example of a JSP (JavaServer Pages) page that uses form-based authentication:
<html> <head><title>Login</title></head> <body> <form action="j_security_check"> Enter your user name: <input type="text" name="j_username"/><br/> Enter your password: <input type="text" name="j_password"/> </form> </body> </html>