ASP and Web Session Management
Michael P. Levy
Senior Consultant, Microsoft Consulting Services
April 2, 1997
ASP Sessions Are Cookie-based
Managing ASP Sessions
When Are Session Cookies Sent by ASP?
ASP Session ID Encryption and Session Security
Hard-to-Guess Cookie Values
Web Farms and ASP Session State
ASP Session Aware Load-Balancing
One of the challenges faced by Web developers is how to create a coherent application out of a series of independent HTML pages. This problem is a particular concern in Web development because HTTP is a stateless protocol. Each browser request to a Web server is independent, and the server retains no memory of a browser's past requests. The HTTP 1.0 protocol did not provide a mechanism to maintain state information between requests from a browser.
To overcome this limitation, application developers require a technique to provide consistent user sessions on the Web. Active Server Pages (ASP) provides powerful and flexible Web session management that requires no special programming. The Session object, one of the intrinsic objects supported by ASP, provides a developer with a complete Web session management solution.
The Session object supports a dynamic associative array that a script can use to store information. Scalar variables and object references can be stored in the session object. A script simply assigns a value to a named entry in the array:
Session ("UserName") = "JohnH"
would store the string "JohnH" in the Session object and give it the name "UserName." This value can be retrieved from the Session object by referencing the Session object by name, as in the following:
Welcome back <% = Session("UserName") %>
Object references are stored using the SET keyword of VBScript, for example:
Set Session("MyAd") = Server.CreateObject("MSWC.Adrotator")
This is similar to the normal Dictionary objects supported by Visual Basic® Scripting Edition (VBScript). However, the Session object will maintain its values for the lifetime of a user's logical Web session. For each ASP page requested by a user, the Session object will preserve the information stored for the user's session. This session information is stored in memory on the server. The user is provided with a unique session ID that ASP uses to match user requests with the information specific to that user's session. In this way, the Session object can be thought of as a locker that a script uses to store information for the user. The session ID is a unique key given to the user that ASP uses to open that locker and retrieve that user's private session information.
ASP uses HTTP cookies to send users their unique session keys. For example, an ASP application that is using sessions would respond to a user's request with an HTTP header such as:
Set-Cookie: ASPSESSIONID=PUYQGHUMEAAJPUYL; path=/Webapp
Each subsequent request by this browser to this server, in the virtual directory /Webapp, would include the HTTP cookie header:
ASP automatically processes this cookie and uses it to restore the values saved in the Session object. The cookie that is sent by ASP as a session ID does not provide an expiration time. Cookies with no expiration specified are only valid until the browser is closed. In this way the cookie is flushed when the user exits the browser.
The server will maintain a user's session data for the lifetime of the session. A script can end a session programmatically by calling the Abandon method of the Session object. When a user completes an application and a session is no longer needed, the script can simply call Session.Abandon to end the session and free the server resources used by that session. A session can also end if the user does not make any HTTP requests to the ASP application for a specified time-out period. This period defaults to 20 minutes, but can be adjusted by setting the Timeout property of the Session object. If a user begins a session, but stops making requests to the Web application, ASP will time out the session after the specified idle period expires. The Session object also exposes a SessionID property. SessionID is a LONG datatype that uniquely identifies the user session.
ASP also provides session-specific events that can trigger your own session initialization and clean up code. Each time a new session begins, the procedure Session_OnStart is called. An ASP program can use this event to perform any required session initialization tasks. Any time a session ends, whether it was timed out by the server, or killed by calling Session.Abandon, the procedure Session_OnEnd is called. An ASP program can use this event to perform any session cleanup that is required.
By default, ASP does not buffer responses. This means that as a script runs, its response output is sent to the client. Since the output is not buffered, all required HTTP headers must be sent to the browser before any HTML is sent. With no buffering, ASP must create a session cookie and send it as an HTTP header before the ASP script generates any HTML output. Even though an ASP script may not require session management, a Session ID cookie is sent for every browser request. In other words, a Session ID cookie is placed in the HTTP headers just in case there is going to be Session state defined in the processing of the ASP file. If no Session state is defined, then the next ASP file request has the same potential to start a Session by storing something in the Session object, so another Session ID Cookie is sent to the browser. ASP will send a different Session ID cookie for each and every ASP file that is requested until Session state is used. Once a script begins a session, by storing a value in the Session object, the Session ID is fixed for that user for the life of the session and no new session ID cookies will be sent.
This behavior may cause some confusion, because ASP applications that don't require session management send out session cookies. If you don't plan to use session management for any applications on the server, then you may use the Registry to turn it off for all applications. You can also turn on ASP buffering to prevent unnecessary cookies. When buffering is used, the ASP response generated by the script is stored in memory and sent all at once. When buffered in memory, ASP can determine whether a session cookie is required before the completed response is sent, and a session cookie header will only be included when it is required.
Once an ASP session begins, a browser request is identified only by its session ID cookie. Any HTTP request with a matching cookie is assumed to have come from the same browser that originated the session. This simple session management approach could open a potential security problem. If a hacker were able to capture, or guess, the session ID cookie in use by an active session, he or she could submit valid HTTP requests that included this cookie. In this manner a hacker could hijack, or steal, a user's active session. For example, if a user had supplied valid credit card information, and an ASP script stored this information in the Session object, a hacker who managed to hijack the session could make purchases using the stolen session. If an application requires strong security, a number of techniques can be employed.
Encrypting all communications between the browser and the server will prevent hackers from capturing the session cookie. Using SSL, all traffic—including the session cookies—are encrypted. A hacker sniffing or monitoring the network will not be able to see the private cookies in use. When using SSL encryption, remember that all browser requests to the Web application directory will include the session ID cookie. Even requests for static HTML content in the same directory tree as the ASP pages will include the session ID cookie. All requests to the virtual directory, not just for ASP files, must be encrypted.
Using encryption prevents hackers from capturing valid cookies. However, a hacker could conceivably guess an active Session ID. ASP Session ID values are selected from a huge range, and are then encrypted. This makes it hard to guess an active cookie. You can obtain higher security by implementing cookies longer than 16 characters and matching them with session variables. Also, keep in mind that if someone does manage to guess a valid cookie once, this will not help them guess another valid cookie.
The following steps are taken when generating ASP session cookies:
- Session ID values are 32-bit long integers.
- Each time the Web server is restarted, a random Session ID starting value is selected.
- For each ASP session that is created, this Session ID value is incremented.
- The 32-bit Session ID is mixed with random data and encrypted to generate a 16-character cookie string. Later, when a cookie is received, the Session ID can be restored from the 16-character cookie string (ASPSESSIONID).
- The encryption key used is randomly selected each time the Web server is restarted.
If the combination of SSL encryption and the complexity of the ASP cookie generation algorithm do not meet your security requirements, user authentication and client certificates can be used in conjunction with session management to provide secure Web applications with client authentication.
ASP session information is stored in memory on the Web server, which creates a challenge for using Active Server Pages in a Web farm environment, where requests are load balanced among a number of Web servers. In order to use the ASP Session object, the same Web server must handle all requests from a user for the life of the session. Most load balancing schemes do not ensure that requests return to the same server.
To use ASP in a load-balanced environment, there are a number of options available, some of which are outlined below:
- Do not use the session management provided by ASP. You can write your own session management logic that keeps session state in a centralized place, such as a database server. This is a good solution; however, you lose the benefits of ASP session management.
- Use a third-party session management ActiveX component. Because ASP can support ActiveX components, it is feasible that a commercial component will be available for ASP that supports session management in a Web farm.
- Load-balance all new requests across your server farm; but once a session begins, make sure that all subsequent requests for that session return to the same server. This technique allows you to load-balance at a logical session level rather than load-balancing each individual request, and is sometimes called "ASP session aware load-balancing."
There are a number of possibilities to implement ASP session aware load-balancing. One simple technique is to load-balance all new requests using current load-balancing mechanisms (for example, round-robin DNS or dedicated IP load balancers). The load balancer will distribute all requests to a site's well known URL. For example, a site could be known by a DNS name such as example.microsoft.com. Any Web requests to this site will be load balanced among the active servers in the Web farm. In the Session_OnStart event that is fired for each new session, the ASP script can use the ASP Response object to send the user an HTTP redirect response. This response can redirect the browser to request an ASP page from the specific server that the user's session is running on. For instance, the redirect URL could specify the IP address, or unique name, of the machine on which the user's session was running. Then all future requests based on relative URLs would return to the machine specified by the redirect.
When a user first comes to a Web application by clicking a hyperlink, or typing a URL such as http://example.microsoft.com/Webapp/, a load balancer routes the request to a specific machine in the Web farm. For example, the load balancer selects a machine named w10.asite.com to handle this request. ASP will create a new session for the request running on this specific machine. In the Session_OnStart event, the ASP script can redirect the user,
<% Response.Redirect("http://w10.sample.com/Webapp/firstpage.asp" %>
The browser will request the specified page, and can run the application. The only requirement is that all URLs embedded in each page (as hyperlinks or form actions) should be relative URLs, for example:
<FORM METHOD="POST" ACTION="ProcessForm.ASP"></FORM> <A HREF="nextpage.asp">
While the user is using the application, all requests will return to the same Web server on which the session is running. This technique may not be appropriate for all Web applications. If users save URLs using favorites or bookmarks, they may save a URL to a specific machine and when they return for a new session, they will not be properly load-balanced. The Session_OnStart logic can choose to accept these requests or intelligently return the user to the well-known URL (http://example.microsoft.com/Webapp/) for load-balancing. This is only an example technique to allow ASP session management to work in a Web farm. Other methods are available. You don't have to do it using a redirect; you can get the same effect by using full URLs in your start page that specify the server the session is on.
Michael P. Levy specializes in Internet and client-server application development. His current focus is helping Microsoft's New York customers design and deploy Internet and intranet solutions.