Use a CAPTCHA-based authentication module for J2EE Web applications The Web application landscape is constantly evolving. What started off as a simple means of sharing documents and information has quickly evolved into a platform for conducting business. Authentication and authorization are critical features for these applications. The landscape has evolved again, and we have returned to community-oriented applications like blogs and wikis. Since authors solicit comments and reader feedback, authentication is not the most critical feature for these applications. In some cases, the fear of identification deters potential contributors. The absence of authentication, however, results in its own set of problems: spam. Sample a few messages found on the Web:“I suppose wiki spam is inevitable. I’ve set up the wiki part of this site to be fairly open to changes to encourage readers and visitors to interact, but now for the second time a spammer has dropped a bunch of links to some Chinese Websites onto the pages.” From X-Pollen.“Note to all wiki spammers: As of 1-2-2005 no changes to this wiki, either by editing or adding new pages, will be picked up by search engines until 10 hours have passed. All spam on this site is usually deleted in minutes, an hour at the most, so it is now pointless to try to add spam of any type to this wiki.” From C2 Wiki.Clearly, spam must be addressed. Most of these malicious attacks happen when the spam-bots are able to figure out data submission patterns. One option for stopping such attacks is to present a challenge that can only be passed by humans and not computers. Enter Turing tests. Named after the famous scientist Alan Turing, these tests determine the capability of machines to perform human-like operations. One of the most famous styles is CAPTCHA(an acronym for completely automated public Turing test to tell computers and humans apart). CAPTCHAs frequently appear as images of fuzzy or distorted letters and numbers that humans can read and respond to, but automated optical-character-recognition software have trouble identifying.Figure 1 shows a typical CAPTCHA. Already, most of the major service providers (Yahoo, Hotmail, Google) are using CAPTCHAs on their free applications, which, to some extent, help them fight spam and fake registrations. In this article, we explore a method for adding CAPTCHA-based authentication to our own Web applications.First, let’s quickly look at the J2EE Web application security model.J2EE security modelSecurity has been one of the focus areas guiding Java’s development. Needless to say, J2EE has adopted the same principles and provides a robust framework for building secure applications. Security in J2EE applications is a vast topic that I can’t cover in detail here. Several excellent resources cover this topic in detail. I strongly recommend that teams and developers spend time familiarizing themselves with these concepts. For this article, I present a 20,000-foot view of some of the key concepts. Key conceptsSecurity in J2EE applications can be enforced by using either a declarative approachor a programmatic approach.As the name suggests, when using the declarative approach, an application developer defines an application’s security constraints outside the application code. These declarations are made in deployment descriptors (web.xml, ejb-jar.xml), and the container’s runtime environment guarantees their enforcement. Declarative security allows developers to:Restrict access to resources only to principals with given roles (e.g., “/admin/*” is restricted to the “administrator” role)Restrict access to URLs only on certain protocols (e.g., “/customer/*” is only accessible via HTTPS)Restrict access to servlets only to principals with given roles (e.g., SiteShutdownServlet is restricted to the “god” role)Restrict individual methods on EJB (Enterprise JavaBeans) components only to principals with given roles (e.g., PurchasingEJBis restricted to the “customer” role)Automatically redirect users to login page when they request a restricted resource, but have not yet logged into the systemOn the other hand, the programmatic approach provides mechanisms for calling and querying the security infrastructure. The developer must enforce these constraints. The programmatic approach helps to: Retrieve the principal associated with the current authenticated user: HttpServletRequest.getUserPrincipalor EJBContext.getCallerPrincipalQuery if the user has been assigned a specific role: HttpServletRequest.isUserInRole(String role)or EJBContext.isCallerInRole(String role)Both these approaches have their own limitations and hence complement each other.Declarative security for Web applicationsDeclarative security for Web applications is passive in nature. This means that only on initial access to the protected resource by an unauthenticated client, is the user forwarded to the login page. If the user is already authenticated and has the necessary authorization (role), he or she is allowed to access the resource.One of the most common approaches to declarative security in Web applications is to use form-based authentication. The Web application deployment descriptor web.xml declares all the necessary elements required for such configuration in two sections. The first section is common to the entire Web application. It identifies:The login method that needs to be used (auth-method). J2EE supports the following authentication mechanisms BASIC, DIGEST, FORM, or CERT.The login and error page for form-based authentication (form-login-config).Superset of all roles that can be used in the application (security-role).Figure 2 shows the key elements of the first section and their relationships.The second section identifies resource-specific constraints. The deployment descriptor can contain zero or more of the following declarations: The section of the site that needs to be protected. This is configured using the url-pattern element within web-resource-collection.The roles that have permission to access this resource (auth-constraint). These roles are generally a subset of roles declared in the first section.The transport guarantees associated with accessing a resource (user-data-constraint).Figure 3 shows the second section’s key elements and their relationships.Let’s walk through a sample web.xml: <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/J2EE/dtds/web-app_2_2.dtd"> < web-app> < !-- ... --> < !-- Define the security constraint. This will limit the /admin/* portion of the application to only be accessible to users within the "admin" role. When an unauthenticated user attempts to access this section of the site, they will be automatically presented with the login page. --> <security-constraint> < !-- Define the context-relative URL(s) to be protected --> <web-resource-collection> <web-resource-name>Protected Area</web-resource-name> <url-pattern>/admin/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> < !-- Define the roles that are allowed to access this URL with the given methods --> <auth-constraint> <role-name>admin</role-name> </auth-constraint> < !-- Transport guarantee could be used to guarantee an HTTPS protocol --> <user-data-constraint> <transport-guarantee>NONE</transport-guarantee> </user-data-constraint> < /security-constraint> < !-- Define the method for authenticating a user when requesting a restricted page. Methods include BASIC (the simple pop-up dialog), FORM and CERTIFICATE. --> <login-config> <!-- We will use form based authentication --> <auth-method>FORM</auth-method> <realm-name>Default Realm</realm-name> < !-- where should the user be forwarded to enter his credentials --> <form-login-config> <form-login-page>/login/login.jsp</form-login-page> <!-- On error the user will be shows this page It can also server side forward back to the login page, which is popular behavior for most sites. --> <form-error-page>/login/error.jsp</form-error-page> </form-login-config> </login-config> < !-- Finally a list of all security roles in the application must be given. --> <security-role> <description>Capable of administrating the site</description> <role-name>admin</role-name> </security-role> </web-app> The sample deployment descriptor contains the following security configuration: Restrict access to URLs that begin with the pattern /admin/*(url-pattern)The resources under /admin can only be accessed using HTTP GET or POST (http-method)The resources can be served over a normal HTTP connection (transport-guarantee)Only users belonging to the “admin” role can access these resources (role-name)Remote users are authenticated using form-based authentication (auth-method)The users will be shown a login page—/login/login.jsp—for entering credentials (form-login-page)If any errors occur during authentication, users will be shown an error page—/login/error.jsp (form-error-page)So far, I have covered the key concepts of the J2EE security model. Let’s now look at ways to extend a container’s security infrastructure.Extending a container’s security infrastructureJAAS(Java Authentication and Authorization Services) implements a version of Pluggable Authentication Modules (PAM) for Java applications. It allows parallel development of authentication technologies and enterprise applications. Application developers can choose from these options and configure them for their applications. JAAS has several benefits since it allows parallel development of authentication technologies and promotes its reuse across different applications.JAAS brings the same value to the application server landscape. However, JAAS has not achieved the same success in the J2EE space. Until recently, application servers exposed custom APIs for extending out-of-the-box security infrastructure. But things are changing. Application servers now provide adaptors for integrating custom JAAS login modules to a security infrastructure. This integration is still application-server-specific and the complexity varies widely. Tomcat provides a relatively easy and straightforward integration with JAAS. The custom login modules are configured using configuration files (Tomcat realm configuration and the standard JAAS configuration). When the server needs to call the login module, it routes all the calls through the org.apache.catalina.realm.JAASRealmadaptor. For more details on integrating JAAS authentication with Tomcat, refer to Resources.In this article, we implement a JAAS login module and later integrate it with the Tomcat server to provide a J2EE security solution.SolutionBefore proceeding to the implementation aspects, let’s understand our solution’s goals and the approach. Our goals are as follows:Provide a weak authentication mechanism for Web applications. Weak authentication means that the authentication module or the application doesn’t differentiate one remote user from another. Once authenticated, all the users have similar roles.Forgo a unique identifier for every user of the system (login name). This guarantees anonymity of the remote user.The authentication mechanism should differentiate between computers and humans to prevent automated spam-bots from misusing the available resources. We will use CAPTCHAs for this test.The authentication mechanism should be based on the J2EE security model. We will avoid other approaches that do not conform to this model.Given the above goals, obviously, we need to certify only that every session belongs to a human user. Application servers maintain sessions to preserve a user’s state. When unauthenticated users access a protected resource, J2EE declarative security diverts them to the login page. The login page generates a unique CAPTCHA and associates it with the user’s session. The login page then presents the CAPTCHA as an image and challenges the user to identify the image. The login page also has a hidden input field that contains the current session ID.The user enters the challenge in the password field and submits the form. On form submission, the login module retrieves the session ID and the user’s response. It then checks the response with the associated CAPTCHA. If these values match, the login module authenticates the user and assigns him the role “anonymous.” After authentication, the remote user can access all the protected resources constrained by the role “anonymous.”Figure 4 illustrates our approach.ImplementationImplementing a new authentication mechanism is fairly straightforward. The entire process breaks down into the following four steps: Protect the Web resourcesGenerate unique tokens for sessionsImplement the JAAS login moduleIntegrate with container’s securityTestLet’s walk through each of these steps.Protect the Web resourcesThe Web resources will be protected using J2EE declarative security. The web.xml snippet that follows below shows the required configuration: <?xml version="1.0" encoding="ISO-8859-1"?> < web-app> < !-- constrain a section of the site --> <security-constraint> <display-name>Anonymous Security Constraint</display-name> <web-resource-collection> <web-resource-name>Protected Area</web-resource-name> <url-pattern>/security/protected/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>anonymous</role-name> </auth-constraint> </security-constraint> < !-- Default login configuration uses form-based authentication --> < login-config> <auth-method>FORM</auth-method> <realm-name>Anonymous Form-Based Authentication Area</realm-name> <form-login-config> <form-login-page>/security/protected/login.jsp</form-login-page> <form-error-page>/security/protected/error.jsp</form-error-page> </form-login-config> </login-config> <!-- Security roles referenced by this web application --> <security-role> <role-name>anonymous</role-name> </security-role> < !-- The Usual Welcome File List --> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> < /web-app> This snippet shows that:All the resources (JSP pages, servlets) kept under the directory /security/protected are secured by the container'anonymous'is the only role authorized to view these resourcesThe container accepts only HTTP method GET and POST requestsThe protected resource can be served over normal HTTP connectionsForm-based authentication is used; /security/protected/login.jsp and /security/protected/error.jsp serve as the login and error pages, respectivelyOnce the declarative security is in place, we need to create the login and error pages. Since we also need to associate a CAPTCHA or token with every session, the login.jsp includes a request to the TokenServlet.Snippet of the login.jsp: ..... ..... <%-- Generates and associates a CAPTCHA --%> <img src="<%= request.getContextPath() %gt;/servlet/AuthToken" alt="Your authentication token"/> < %-- The form login page --%> <form method="POST" action='<%= response.encodeURL("j_security_check") %>' > <table class="legacyTable" border="0" cellspacing="5"> <input type="hidden" name="j_username" value="<%= session.getId() %>"> <tr> <th align="right">Challenge:</th> <td align="left"><input type="password" name="j_password"></td> </tr> <tr> <td align="right"><input type="submit" value="Log In"></td> <td align="left"><input type="reset"></td> </tr> </table> </form> ..... ..... The login page uses form-based security and submits data to j_security_check. The j_username input field is hidden and defaults to the user’s session ID. The other input field, j_password, is where the user enters the token.The error page is pretty simple. It simply states that a problem occurred while authenticating the session and provides a link to the login page.Generate unique tokens for new sessionsAfter protecting Web resources, we need to provide a mechanism for generating and associating tokens with sessions. To support multiple token-generation implementations, we use the Abstract Factory pattern. jw.token.factory.TokenFactoryis the abstract factory, and, based on the parameters, it initializes and returns a concrete token factory. If no specific token factory has been requested, it returns a default implementation. There are two implementations of the token factory in the distribution available for download from Resources. The first one is my amateur attempt at creating CAPTCHA’s jw.token.factory.SimpleTokenFactory. The second is a more mature implementation provided by the JCaptcha project, jw.token.factory.JCaptchaTokenFactory. Both the token factories return a specific implementation of jw.token.Tokenwhen the getToken() method is invoked. The Token object is a container for the CAPTCHAs and can be rendered as images by calling the getTokenImage.Figure 5 shows the token factory’s object model.Once the remote client is forwarded to the login page, it calls the jw.jaas.servlet.TokenGeneratorServlet to retrieve a token. The TokenServlet acts as a gatekeeper for all token-generation requests and has been configured in web.xml. It accepts an initialization parameter, tokenFactory, which indicates the token factory to be used. The servlet delegates the call to the factory, obtains the token, and later streams the token as an image to the remote Web client.Figure 6 shows the token generation sequence.To store tokens associated with the session, we create a cache. The jw.token.AuthenticationTokenCache acts as a repository for all generated tokens and provides an interface to add a new token, query for a token, and finally remove a token associated with a session. The cache is a singleton and uses a synchronized map.Periodically, we also need to clean up the AuthenticationTokenCache. The jw.token.TokenInvalidationListenerhas been registered as a session listener in the web.xml. Hence, whenever a session is destroyed, the sessionDestroyed() method is called and the token cache is cleared.Figure 7 shows the object model for the servlet, cache, and invalidation listener.The web.xml showing servlet configuration: <web-app> ..... ..... < listener> <listener-class>jw.jaas.servlet.TokenInvalidationListener</listener-class> </listener> < !-- Standard Action Servlet Configuration (with debugging) --> <servlet> <servlet-name>tokengen</servlet-name> <servlet-class>jw.jaas.servlet.TokenGeneratorServlet</servlet-class> <load-on-startup>2</load-on-startup> <init-param> <param-name>tokenFactory</param-name> <param-value>jw.token.factory.SimpleTokenFactory</param-value> </init-param> </servlet> < servlet-mapping> <servlet-name>tokengen</servlet-name> <url-pattern>/servlet/AuthToken</url-pattern> </servlet-mapping> ..... ..... </web-app> In the deployment descriptor, the path /servlet/AuthTokenis mapped to the TokenGeneratorServlet. The servlet has been configured to use SimpleTokenFactory.Implement the JAAS moduleThe steps for implementing a JAAS login module are well documented in the JAAS Authentication Guide. The login module contains lifecycle methods like initialize(), login(), logout(), abort(), and commit(), which are invoked by a login context. Most containers provide adaptors for plugging in JAAS login modules within their security infrastructures. Tomcat provides JAASRealmfor this purpose. It intercepts the login form submission and creates a callback handler, JAASCallbackHandler, capable of handling two callbacks, NameCallback and PasswordCallback. These callbacks contain the value of j_usernameand j_password input fields, respectively. The callback handler is then passed to the login module to perform authentication.Figure 8 shows the object model for the login module classes.Figure 8. Class diagram for JAAS classesMost of the methods (commit(), abort(), initialize(), logout()) on the login module are self explanatory. Let’s look closer at the login() method.The login() method retrieves the session ID and user’s response from the callback handler. It then queries the AuthenticationTokenCacheto retrieve the Tokenassociated with the session. The user’s input is compared with the internal state of the Token object. If the comparison succeeds, the user’s session is associated with a principal called AnonymousPrincipal. Since the application is interested only in weak authentication, you can consider this similar to naming every user on your site “anonymous.”The login context then commits the process and confirms that the user has logged in. In commit(), we simply add the principal to the subject representing the remote user. The user is now logged into the application with a principal anonymous.Figure 9 shows login()‘s sequence diagram.Integrate with the container’s securityWe now need to integrate all the pieces with the container’s security infrastructure. This is a server-specific step. Tomcat maintains a configuration file for every deployed Web application. This configuration file is named <applicationname>.xmland is located under the <TOMCAT_HOME>/conf/Catalina/localhostfolder. Replace the file’s contents with the following lines: <?xml version='1.0' encoding='utf-8'?> <Context debug="9" docBase="d:/work/captcha-login/web" path="/clogin"> <Realm className="org.apache.catalina.realm.JAASRealm" appName="clogin" debug="99" roleClassNames="jw.jaas.AnonymousPrincipal" userClassNames="jw.jaas.AnonymousPrincipal"/> </Context> In addition, we also require the JAAS configuration directive to associate the login module with the application.JAAS configuration file: clogin { jw.jaas.AnonymousLoginModule required debug=true; }; For convenience, the bundled build scripts automatically create all these configuration entries.TestFor testing the security module, I have packaged a Web application called Anonymous Bulletin Board (ABB). The bulletin board is a place where users can discuss the topic of the day. The application’s homepage lists all the messages posted so far. A user can also contribute by posting messages. The pages that allow posting are protected by declarative security. Users are authorized to post messages only after they enter the right challenge. Figure 10 shows screen shots of the application for this scenario.PrerequisitesTo build and test the application, we need Tomcat Web container version 5.0 or higher and the Ant build environment version 1.5 or higher. These can be downloaded and installed from Apache. Once we have all the prerequisite components in place, we can build and deploy the application by following the steps outlined below.Installation and testingDownload and unzip the jw-0307-captcha.zip file to a directory (e.g., d:captcha). The unzipped archive will have folders for the source code, dependent libraries, Web application, and the build scripts.Navigate to the exploded directory (d:captcha) and edit the file setAntEnv.cmd. As the name suggests, this file contains variables needed by the build environment to start up. Edit the values of the ANT_HOME and JAVA_HOMEvariables to point to the correct location. All the other properties are derived from these two variables and hence can be left untouched.Navigate to the exploded directory (d:captcha) and edit the file ant.developer.properties. This file contains properties that influence the way the application is built. Most of the properties in the file have meaningful default values. You will need to set appropriate values only for the properties build.homeand server.home.Now open a command shell (cmd), navigate to the exploded directory (d:captcha), and execute the setAntEnv.cmd batch script.Build and deploy the application by typing ant compile jar deploy. The Ant script first creates the required directories, compiles and JARs the security module code, and finally deploys it.Start the application server by typing ant start. Tomcat should start and spawn a separate window.Open a browser and test the application by pointing it to http://localhost:8080/clogin (clogin is the application.nameproperty configured in ant.developer.properties).After testing, you can stop the server by closing the Tomcat server window.Whenever you change any property in ant.developer.properties, do a clean build. This ensures that all properties are correctly propagated. For example, if you use the out-of-box configuration, the token servlet will use the simple token factory. You can also test the more sophisticated CAPTCHA implementation by changing the value of captcha.token.factoryto jw.token.factory.JCaptchaTokenFactoryin the properties file ant.developer.properties. Run ant clean-all deployto propagate the changes. Start the application server and test the application.DebuggingDepending on your environment, the application may fail to launch properly. The build scripts have deployed a log4j configuration file that configures Tomcat’s logging. You can look at trace- and debug-level messages by opening the file <TOMCAT_HOME>/logs/jaasModule.logor <TOMCAT_HOME>/logs/tomcat.log. The jaasModule.logcontains debug information specific to the login module and the Web application. tomcat.log catches all the log messages originating from the container and the deployed code. Open these files to look for clues.ConclusionIn this article, we explored the J2EE Web application declarative security model and ways to extend it using JAAS login modules. We extended Tomcat’s J2EE security infrastructure to support a custom authentication mechanism. The authentication mechanism was implemented as a JAAS login module and used CAPTCHAs to weakly authenticate users. To a certain extent, it also guaranteed that the remote user is human. By using the container’s declarative security support, we easily secured an application without any modification to the codebase.It is important to note that CAPTCHAs can create usability issues for people with disabilities. Web application developers should be aware of these issues when choosing their authentication technologies.Anand Ramanworks for Sapient out of its Delhi office as a senior associate, technology. He has been working on Java- and J2EE-related technologies for the past five years. Tackling J2EE complexity is a matter close to his heart. JavaAccess ControlSecurity