JavaEnterprise JavaState and session tracking with Java servlets Part 2: Securing data

State and session tracking with Java servlets Part 2: Securing data

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.


In the second part of this tutorial on state and session management, we’ll further examine how cookies can be used to maintain state information between servlet requests, as well as some of the security implications of storing state data in cookies. We’ll also examine the topic of session management, and the advanced session-tracking features that the Servlet API offers.

Long-lasting cookies

By default, the cookies created by Java servlets will expire when the browser exits. Often, however, it’s handy to have this data persist for days, weeks or even months later. For example, a cookie that tracks user preferences should remain until the next visit. We can manually specify an expiration date, using the

Cookie.setMaxAge( int )
method.

// Store color preference for one year
Cookie cookie = new Cookie ("color", "blue");
cookie.setMaxAge ( 60 * 60 * 24 * 365 );

// Add cookie to response
response.addCookie (cookie);

Security implications of storing state information in cookies

When dealing with mundane details, like a counter, it’s perfectly safe to store data in cookies. However, if the state information contains unique identifiers, such as a User-ID, then it can represent a strong security risk. If that User-ID authorizes access to a particular resource, then anyone who runs the Web browser can gain access. Under ideal situations, only a single user has access to a particular machine, but in shared environments (such as Internet cafe terminals, or offices), other people can access the browser and masquerade as a particular user.

This poses a potential security risk — but one that can be guarded against. As mentioned earlier, it is possible to specify an expiration date for the cookie. Once the expiration date is reached, the cookie is discarded from the browser. By default, cookies created by Java servlets will expire once the browser exits. However, in a shared environment where browsers remain open, this represents too great a security risk. The solution is to specify a very short expiration date (for example, in two minutes) and to periodically refresh the cookie as new requests come in. 

// Create a cookie containing the userID
Cookie cookie = new Cookie ("userID", getUserID() );

// Specify a three minute timeout
cookie.setMaxAge ( 180 );

// Send back to the browser
response.addCookie(cookie);

For added security, it is also important to provide a "logout" function. This allows users to close their account, so that no further access can be gained without specifying a password or being authenticated. This is important as it is possible for another user to come along before the cookie has expired (though forgetful users still have the safeguard of a timeout). To void a cookie, simply specify a maximum age of zero.

// Void the cookie and send it back to the user
userid_cookie.setMaxAge (0);
response.addCookie(userid_cookie);

For simple tasks, cookies are a useful mechanism for storing simple state data. To store data securely, however, involves extra effort. A better mechanism is session management. 

State versus session management

Session management allows a "virtual connection" to be established between browser and servlet. Rather than storing state information in cookies, hidden fields, or hyperlinks, a session identifier is assigned to each user. This identifier is then used to track individual requests and match them to a session. However, sessions persist for a limited period of time and are associated with a single user. No state information needs to be sent across the network — such information can be stored within the servlet and looked up as required.

This approach offers two big advantages over stored state variables in forms, hyperlinks, or cookies:

  • simplicity for the developer
  • less network utilization, as state data is not sent repeatedly.

The session approach is simpler for the developer, as there is no need to constantly refresh cookies or to write code to expire them. As state information isn’t sent to the browser and then echoed back to the server, less bandwidth is consumed. The Servlet API provides support for session-tracking, in the form of the

javax.servlet.http.HttpSession
class.

Overview of HttpSession

Session-tracking is made extremely easy with the

HttpSession
class. Developers don’t need to worry about generating a unique session ID or storing it. For browsers that support cookies, a temporary identifier will be issued, and then echoed back by the browser every time a request is made. If cookies are not supported, then a servlet can use the technique of URL re-writing to encode a session identifier in every hyperlink. For most situations, however, cookies are sufficient for storing a session identifier.

Creating an HttpSession

Creating a new session is relatively straightforward. The

getSession(boolean)
method of

javax.servlet.http.HttpServletRequest
will return the current session. The

boolean
parameter to this method specifies whether a new session should be created if one does not already exist. So to create a new session, or obtain an existing one, the code remains the same.

// Get the session for this client
HttpSession session = request.getSession(true);

Storing and retrieving data from a HttpSession

Once a session has been established, state data can then be added or removed. Like cookies, it is represented by a key-value pair. A single key maps to a single object — but this object can be an array if storing multiple values is necessary.

// Store userID in current session
session.putValue ("userID", request.getParameter("userid") );

Retrieving values is almost as easy. Using a string key, the contents of state data can be returned using the

getValue(String)
method. However, the return type for this method is Object, so it is often necessary to cast to a specific class.

// Return the list of items in shopping trolley
Vector vec = (Vector) session.getValue("list");

// Check to see if value exists
if (vec != null)
{
// Code to process contents would go here …
}

Other issues with HttpSession

By default, session-tracking is accomplished using cookies. This process is transparent for the developer — no extra code to store and retrieve cookies need be written. However, if cookies are disabled or not supported, every hyperlink must be encoded to include a session identifier. Without this, the session becomes lost. The

HttpServletResponse.encodeUrl(String)
method is used for this purpose. However, the details of this encoding is specific to each web-server or servlet engine, and not every server supports this feature. This feature should be viewed as a last resort – it is preferable for cookies to be used instead. If URL-rewriting is supported though, it will allow older browsers without cookie support to access the servlet. As an example, to encode a reference to a servlet, we could use the following code:  

// HttpServletResponse.encodeUrl adds session data automatically
response.encodeUrl ( "/servlets/MyServlet" );

Developers should also be mindful of the fact that sessions are for storing temporary data, not for data that lasts for a long period of time. Sessions will not persist once the browser or servlet terminates, so any important data should be saved to disk or to a database. To allow easy retrieval of data at a later date, a username/password could be established, and a session created once the identity of the user is authenticated. Any changes to state data would be saved once the session closes, and accessed again at a future point in time.

Putting session-tracking to work

To demonstrate session-tracking with the

HttpSession
class, I’ve written a simple application that records user color preferences. A new session is created immediately, and state data for the background and foreground page colors is added to the session. Changes can be made to state data, and these changes are retrieved each time a page request is made. A form is provided, to allow customization of state data. In addition, a hyperlink is also provided, to demonstrate that even when no CGI parameters are specified, state data is being retrieved. Figure 1 shows the servlet in action, when new values have been specified as CGI parameters. If cookies are enabled, or URL-rewriting is supported, clicking on the URL retains customized color preferences.

NB: To test URL-rewriting, cookie support must be disabled, and the servlet engine must be capable of supporting it.

Listing 1: HttpSessionExample.java (See page 2)

Figure 1. Demonstration of session-tracking using HttpSession.

Summary

Whether you choose to create your own session-tracking mechanism using cookies or to use the

HttpSession
class, the Servlet API makes it easy to maintain state information across HTTP connections. For storing simple state information, cookies represent an excellent choice. However, for servlets where security is a concern, care must be taken to specify an adequate timeout value. For larger-scale projects, developers may also benefit from the existing session-tracking features provided by the Servlet API.  

About the author

David Reilly is a software engineer and freelance technical writer living in Australia. A Sun Certified Java 1.1 Programmer, his research interests include the Java programming language and networking & distributed systems. He can be reached via e-mail at java@davidreilly.com.



  Listing 1
HttpSessionExample.java
by David Reilly.

 

// Import I/O & servlet packages
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HttpSessionExample extends javax.servlet.http.HttpServlet
{
// Implementation of GET request
public void doGet (HttpServletRequest request, HttpServletResponse response) throws IOException
{
// Assign a content type
response.setContentType( "text/html" );

// Prevent caching of server-side responses
response.setHeader( "Cache-Control" , "no-cache" );

// Create a stream for writing HTML output
PrintStream pout = new PrintStream (response.getOutputStream());

// Get the user session, and create one if one doesn't already exist
HttpSession userSession = request.getSession(true);

// Check for presence of state data in userSession
String background = (String) userSession.getValue("background");
String foreground = (String) userSession.getValue("foreground");

if (background == null)
{
// No background stored - place default value in session
background = getDefaultBackground();
userSession.putValue("background", background);
}
if (foreground == null)
{
// No foreground stored - place default value in session
foreground = getDefaultForeground();
userSession.putValue("foreground", foreground);
}

// Next, check for a change in parameter from FORM
if ( request.getParameter("background") != null)
{
background = request.getParameter("background");
userSession.putValue ("background", background);
}
if ( request.getParameter("foreground") != null)
{
foreground = request.getParameter("foreground");
userSession.putValue ("foreground", foreground);
}

pout.println ("<HTML><HEAD><TITLE>HttpSessionExample</TITLE></HEAD>");
pout.println ("<BODY BGCOLOR='" + background + "' TEXT='" + foreground + "'>");
pout.println ("This is an example of a servlet that uses HttpSession to store state info <p>n");

// Print form
pout.println ("<form action='" + response.encodeUrl(request.getRequestURI()) + "' method=get>n");
pout.println ("Background : <input type=text name=background value='" + background + "'><br>n");
pout.println ("Foreground : <input type=text name=foreground value='" + foreground + "'><br>n");
pout.println ("<input type=submit>");
pout.println ("</form>");

pout.println ("<a href='" + response.encodeUrl ( request.getRequestURI() ) + "'>Hyperlink example of URL rewriting</a> - not supported by all servers");<!--Broken Link? -->
pout.flush();

}

// Implemention of POST request
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException
{
// Pass to doGet
doGet(request,response);
}

public String getDefaultBackground() { return "white"; }
public String getDefaultForeground() { return "black"; }
}

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories