Secure a Web application, Java-style

Use Java's multiple-layer security implementation to protect your Web

Web security can be defined in various ways, depending on individual points of view. The main focus of security in this article is the safety of applications developed and deployed for the Internet. Here, I will outline some software security measures that you can take to secure your application. While none of them is completely infallible, combining these approaches with hardware can help prevent malicious attacks on your business.

The two main concepts of security are authentication and authorization. I will describe each of them in the following sections and provide examples of how you can implement them in your applications. In addition, I will discuss some key classes of the Java Security API to prepare for a more detailed example that combines form-based authentication with Java's security model. The concepts outlined in the example should enable your enterprise to produce a security policy for your Java-based applications.

Authentication

Authentication is the process by which users' access privileges are verified prior to their entering a Website's protected area. There are two major authentication approaches: basic authentication and form-based authentication.

Basic authentication

Basic authentication relies on the Web server for authentication to protected areas. Sites protected by basic authentication let the user browse through unprotected areas without requiring the user to enter a password. However, the browser will automatically prompt the user for a password and username should he or she attempt to access a secure page. This prompt comes in the form of a dialog box.

The username and password combination is then encoded (base 64) and passed in an unencrypted form to the Web server. The Web server compares the encoded value against values stored in a flat file, a database, or a directory server.

If the user is authenticated, the server then verifies that the user has the privilege to access the requested page against a file, such as httpd.conf. If the user has access, the Web server then serves the page. If the user is denied access, the server either requests the username/password combination again or presents an error message on the browser window.

Because the actual syntax of the basic authentication varies between servers, I do not present any here. There are numerous Web resources describing the syntax of the various servers.

Form-based authentication

The majority of sites use an approach called form-based lazy authentication, which lets users navigate through unprotected areas of the site without requiring a password. Only when the user wants to access protected areas, such as ordering or account status, does the site present a login form. This is the most common security approach and is used by large e-commerce firms, such as Barnes & Noble. The benefit of this approach is that users are not subjected to the wait times associated with authentication unless they truly need access to a protected page.

As the most common security scheme on the Web, form-based lazy authentication lends itself to the example presented in this article.

Use forms to authenticate clients

A common way for servlet-based systems to perform authentication is to use the session to store information indicating that a user has logged into the system. In this scheme, the authentication logic uses the HttpSession object maintained by the servlet engine in the Web server.

A base servlet with knowledge of authentication is helpful in this case. Using the service method of the BaseServlet, the extending servlets can reuse the security check functionality. All code used in this example can be found in Resources.

The service method is shown in the following code snippet:

   public void service(HttpServletRequest request, HttpServletResponse
response)
      throws IOException, ServletException
   {
      // check to see if a session has already been created for this user
      //   don't create a new session yet.
      HttpSession session = request.getSession( false);
      String requestedPage = request.getParameter(Constants.REQUEST);
      if ( session != null)
      {
         // retrieve authentication parameter from the session
         Boolean isAuthenticated = (Boolean)
session.getValue(Constants.AUTHENTICATION);
         // if the user is not authenticated
         if ( !isAuthenticated.booleanValue() )
         {
            // process the unauthenticated request
            unauthenticatedUser(response, requestedPage);
         }
      }
      else // the session does not exist
      {
         // therefore the user is not authenticated
         // process the unauthenticated request
         unauthenticatedUser(response, requestedPage);
        }
   }

Notice that you can expand this method to perform other generic functions as well. In this example, I developed only the security aspects of this class.

The BaseServlet attempts to retrieve the session from the servlet engine. On retrieval, the servlet verifies that the user has been granted access to the system. Should either of these checks fail, the servlet redirects the browser to the login screen.

On the login screen, the user is prompted for a username and password. Note that the data passed from the browser to the Web server is unencrypted unless you use Secure Socket Layer (SSL).

The LoginServlet uses the username/password combination to query the database to ensure that this user does indeed have access to the system. If the check fails to return a record for that user, the login screen is redisplayed. If the check is successful, the following code stores the user authentication information inside a session variable.

   // create a session
   session = request.getSession( true);
   // convert the boolean to a Boolean
   Boolean booleanIsAuthenticated = new Boolean( isAuthenticated);
   // store the boolean value to the session
   session.putValue(
      Constants.AUTHENTICATION,
      booleanIsAuthenticated);

In this example, it's assumed that any user who successfully authenticates to the system has access to the pages displayed prior to login. However, there are cases in which the application development team may require a more refined security approach to satisfy its requirements.

Authorization

Authorization verifies that the security policies protect against more sophisticated hackers by preventing unauthorized code from connecting to back-office systems, such as Enterprise JavaBeans (EJB). There are two types of authorization: code authorization and caller authorization.

Code authorization

Your security team can prevent unauthorized code use by limiting the classes available to the virtual machine used by the servlet engine. You can achieve this process of code authorization by removing unnecessary entries from the classpath.

The security team has complete control over which code should be included in the classpath and can therefore authorize the code prior to inclusion. The team should ensure that the code does not have access to third-party tools or extraneous code.

Caller authorization

In addition to code authorization, you can also authenticate the caller of back-office systems. The EJB model, for example, lets the development team specify a username and password for access to any bean deployed in a container.

Any code that attempts to connect to these beans must pass the username/password combination to the container for authorization. A failed authorization results in an exception that prevents the caller from completing its action.

Authorization example

In this second example, I'll explore the mechanisms built into the EJB specification for preventing unauthorized access to the business logic. While this example is based on the WebLogic Tengah server, the concepts hold true for other servers even though the syntax will differ. See Resources for the code or configuration files used in this example.

In the EJB's deployment descriptor, the following code identifies the access control entries associated with the bean:

(accessControlEntries
DEFAULT [administrators basicUsers]
theRestrictedMethod [administrators]
); end accessControlEntries

Notice that two user categories have been identified. Administrators have access to the bean by default and constitute the only user group that has access to theRestrictedMethod.

Once you've authorized that the administrators have access to the bean, you now need to create properties detailing which users are in the administrators group. To do this, the weblogic.properties file must include the following lines:

weblogic.password.JoeAdministrator=joe
weblogic.security.group.administrators=JoeAdministrator
weblogic.password.JaneBasic=jane
weblogic.security.group.basicUsers=JaneBasic

At this point, you've established the users who have access to the bean and have restricted certain specific methods. This helps limit the potential for malicious attacks on your Web server to reach the business logic stored in the beans.

The last step in this EJB authorization example is to establish the client connection to the bean. In this case, the client must specify the username/password combination properly in order to have access to the restricted bean or methods. An example client communication follows:

try{
Properties myProperties = new Properties();
myProperties.put( Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.T3InitialContextFactory");
myProperties.put(Context.PROVIDER_URL, "t3://localhost:7001");
myProperties.put(Context.SECURITY_PRINCIPAL, "JoeAdministrator");
myProperties .put(Context.SECURITY_CREDENTIALS, "joe");
ic = new InitialContext(myProperties);
...
}
catch (Exception e) { ... }

Since you've passed the JoeAdministrator user to the InitialContext, you'll have access to any method to which the administrators group has been granted access. If your application makes connections to external beans or your beans are used by external applications, you should implement this security authorization.

Java security

In the Java Security API, there is a package, java.security.acl, that contains several classes that you can use to establish a security system in Java. These classes enable your development team to specify different access capabilities for users and user groups. All code used in this example can be found in Resources.

The concept is fairly straightforward. A user or user group is granted permission to functionality by adding that user or group to an access control list. For example, consider a java.security.Principal called testUser as shown below:

Principal testUser = new PrincipalImpl ("testUser");

Now you can create a Permission object to represent the capability of reading from a file.

Permission fileRead = new PermissionImpl ("readFile");

Once you have created the user and the user's permission, you can create the access control list entry. It's important to note that the security APIs require that the owner of the access list be passed in order to ensure that this is truly the developer's desired action. It is essential that this owner object be protected carefully.

Acl accessList = new AclImpl (owner, "exampleAcl");

In its final form, the access list will contain a bunch of access list entries. You can create these as follows:

AclEntry aclEntry = new AclEntryImpl (testUser);
aclEntry.addPermission(fileRead);
accessList.addEntry(owner, aclEntry);

The preceding lines create a new AclEntry object for the testUser, add the fileRead permission to that entry, and then add the entry to the access control list.

You can now check the user permissions quite easily, as follows:

boolean isReadFileAuthorized = accessList.checkPermission(testUser,
readFile);

Real-world example

The preceding examples show how a form-based authentication scheme can work, how the EJB model can restrict access based on a username/password combination, and how you can use the access control lists within the Java security model to restrict access to functionality. I now intend to leverage these examples to demonstrate an overall system approach to the security problem.

In the first example, the form-based authentication scheme was implemented in a rudimentary fashion. It checked only to ensure that the user was contained in the database of authenticated users. A user contained in the database was granted access to all functionality within the system without further definition.

In the second example, the EJB authorized the user attempting to execute restricted methods on the bean. This security protects the bean from unauthorized access, but does not protect the Web application.

In the third example, I discussed the Java Security Access Control package. I demonstrated how you could use a simple API to verify that a user had access to certain functionality within the system.

By gathering these three examples, you can create a simple authentication scheme that limits the user's access to Web-based components of a system, including back-office systems.

The goal of this next example is to demonstrate the collective power of these three security mechanisms and to provide a pattern by which you can build more advanced security frameworks. All code used in this example can be found in Resources.

1 2 Page
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more