Java Web Programming with Eclipse contents
Last modified February 06, 2011 01:49 am

back next

Web Application Security (continued)

Password Digests

Video

Overview

If the web application server is compromised (i.e. an attacker has gained access to the server), it is possible for the attacker to get the usernames and passwords from the user table. Even if your application doesn't have highly confidential information, users may have entered passwords that they use to access other applications. To mitigate the potential danger, it is better to store secure digests of user passwords rather than the plaintext (unencrypted) password.

The key property of a secure digest is that it is an irreversible mapping of a string to another string, which means that given the password, you can compute the digest; but given the digest, you can not compute the password. If we store a digest of the passwords in the database, then an attacker who has access to the database can not compute any passwords. If the passwords are simple, the attacker could perform what is referred to as a dictionary attack in which he or she tries every password from a list of common or potenital passwords. This threat is mitigated if users choose string passwords.

The web application also can not determine the user passwords from what is stored in the database. However, the password digests are sufficient for authentication. When the user submits a password during authentication, The web application maps the password to its digest and then checks to see if the digest it has just computed matches with the digest stored in the database.

It should be kept in mind that storing digests in the database simply adds a layer of inconvenience to an attacker, and does not provide essential security. If the attacker has access to the database, he or she most likely has access to the the system on which the web application is running. In this case, the attacker can put up an intermediate login page that captures the users' passwords before passing them to the web application. Other attacks are possible as well if the underlying server is not secure or if the web application provides an entry point into the system in some way.

In this section, we add password digesting to the publisher application.

Secure Digester Utility Class

In the publisher.data package, create a class called SecureDigester with the contents of the following listing. This class can be used to compute secure digests of any string; in our case, we use it to compute the digests of passwsords.

package publisher.data;

import java.security.MessageDigest;

public class SecureDigester
{
   private static final char digits[] =
   { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
         'F' };

   private static String byteArrayToHexString(byte[] b)
   {
      StringBuffer hexString = new StringBuffer(b.length);
      for (int i = 0; i < b.length; i++)
      {
         hexString.append(digits[(b[i] & 0xF0) >> 4]);
         hexString.append(digits[b[i] & 0x0F]);
      }
      return hexString.toString();
   }

   public static String digest(String plaintext)
   {
      try
      {
         MessageDigest md = MessageDigest.getInstance("SHA");
         md.update(plaintext.getBytes("UTF-8"));
         byte[] mdBytes = md.digest();
         String hashString = byteArrayToHexString(mdBytes);
         return hashString;
      } catch (Exception e)
      {
         throw new RuntimeException(e);
      }
   }

   public static void main(String args[])
   {
      String password = args[0];
      System.out.println(password + " -> " + digest(password));
   }
}

Notice that SecureDigester has a main method. This allows you to run the digester outside the web application for the purpose of generating digests of passwords to include in test data.

The digest function of SecureDigester is the public entry point into the class that will be called by the login servlet to computer password digests. As you can see from the code, the digest method uses the MessageDigest class that is part of the core Java API. MessageDigest has a factory method called getInstance that takes a string that names the hash function to use. In our case, we specify SHA, which is a commonly used secure hash function. The code passes the plaintext string into the update method of the message digest object as a byte array of UTF-8 character codes. Then, the digest method is invoked on the message digest object in order to compute the SHA digest of the byte array. This returns a byte array, which is then transformed into a string by a call to the locally defined byteArrayToHexString. It is this string that the application uses as its version of the password.

Now, modify the login servlet by replacing the password checking code with the following code. Remember to organize imports to resolve the name SecureDigester.

      String password = req.getParameter("password");
      if (password == null)
      {
         logger.debug("authentication failed: no password");
         req.setAttribute("message", "Authentication failed.");
         jsp.forward(req, resp);
         return;
      }
      String passwordDigest = SecureDigester.digest(password);
      if (!user.getPassword().equals(passwordDigest))
      {
         logger.debug("authentication failed: bad password");
         req.setAttribute("message", "Authentication failed.");
         jsp.forward(req, resp);
         return;
      }

At this point, in order to login, we need to replace the passwords in the database with their digests as computed by the secure digester class. To do this, we run the secure digester class as a stand-alone program and pass in the password as a command line argument to the program. To do this inside of Eclipse, we create a run configuration that specifies the secure digester class as the entry point of execution and enter the password that we wish to encrypt as a program argument. Form the main menu, select Run ... Run Configurations. Navigate to the publisher project and secure digester main class if these fields are not already set correctly. The following screen shot shows the reult.

Run Window

Select the arguments tab and enter into the program arguments box the password that you wish to encrypt. See the following screen shot shows the string admin being passed in as the password to digest.

Arguments tab of Run window

After running the secure digester to encrypt a password, look in the console view to see the result. If you entered the string admin, the program would generate the following output in the console view.

admin -> D033E22AE348AEB5660FC2140AEC35850C4DA997

Open the insertdb.sql script and replace the password value of the user with the hex string produced by the secure digester and run the database build file to re-build the database.

Restart the publisher application and verify that the application works correctly.

back next

Copyright 2007-2009 David Turner and Jinseok Chae. All rights reserved.