ASP.NET State Management : Working with a Session’s State (part 1) – Identifying a Session

Now that you have grabbed hold of the session state basics, we can sharpen our skills by looking into more technically relevant aspects of session state management. Handling session state is a task that can be outlined in three steps: assigning a session ID, obtaining session data from a provider, and stuffing it into the context of the page. As mentioned, the session state module governs the execution of all these tasks. In doing so, it takes advantage of a couple of additional components: the session ID generator and session state provider. In ASP.NET 2.0 and beyond, both can be replaced with custom components, as we’ll discuss later. For now, let’s tackle some of the practical issues you face when working with session state.

1. Identifying a Session

Each active ASP.NET session is identified using a 120-bit string made only of URL-allowed characters. Session IDs are guaranteed to be unique and randomly generated to avoid data conflicts and prevent malicious attacks. Obtaining a valid session ID algorithmically from an existing ID is virtually impossible. In ASP.NET 1.x, the generator of the session ID is a system component buried in the framework and invisible from outside. In ASP.NET, it has become a customizable component that developers can optionally replace.

Note

An old proverb reminds us that nothing should be done only because it is doable. This motto is particularly apt here as we talk about parts of the session state management that are customizable in ASP.NET. These subsystems, such as the session ID generator, should be customized only with good reason and with certainty that it won’t make things worse or lower the level of security. I’ll return to this point in a moment with more details.

Generating the Session ID

A session ID is 15 bytes long by design (15×8 = 120 bits). The session ID is generated using the Random Number Generator (RNG) cryptographic provider. The service provider returns a sequence of 15 randomly generated numbers. The array of numbers is then mapped to valid URL characters and returned as a string.

If the session contains nothing, a new session ID is generated for each request and the session state is not persisted to the state provider. However, if a Session_Start handler is used, the session state is always saved, even if empty. For this reason, and especially if you’re not using the in-process session provider, define Session_Start handlers with extreme care and only if strictly necessary.

In contrast, the session ID remains the same after a nonempty session dictionary times out or is abandoned. By design, even though the session state expires, the session ID lasts until the browser session is ended. This means that the same session ID is used to represent multiple sessions over time as long as the browser instance remains the same.

Session Cookies

The SessionID string is communicated to the browser and then returned to the server application in either of two ways: using a cookie or a modified URL. By default, the session-state module creates an HTTP cookie on the client, but a modified URL can be used—especially for cookieless browsers—with the SessionID string embedded. Which approach is taken depends on the configuration settings stored in the application’s web.config file. By default, session state uses cookies.

A cookie is really nothing more than a text file placed on the client’s hard disk by a Web page. In ASP.NET, a cookie is represented by an instance of the HttpCookie class. Typically, a cookie has a name, a collection of values, and an expiration time. In addition, you can configure the cookie to operate on a particular virtual path and over secure connections (for example, HTTPS).

Important

ASP.NET 2.0 (and newer versions) takes advantage of the HTTP-only feature for session cookies on the browsers that support it. For example, it is supported on Internet Explorer 6.0 SP1 or with Windows XP SP2 installed. The HTTP-only feature prevents cookies from being available to client-side script, thus raising a barrier against potential cross-site scripting attacks aimed at stealing session IDs.

When cookies are enabled, the session-state module actually creates a cookie with a particular name and stores the session ID in it. The cookie is created as the following pseudocode shows:

HttpCookie sessionCookie;
sessionCookie = new HttpCookie("ASP.NET_SessionId", sessionID);
sessionCookie.Path = "/";

ASP.NET_SessionId is the name of the cookie, and the SessionID string is its value. The cookie is also associated with the root of the current domain. The Path property describes the relative URL that the cookie applies to. A session cookie is given a very short expiration term and is renewed at the end of each successful request. The cookie’s Expires property indicates the time of day on the client at which the cookie expires. If not explicitly set, as is the case with session cookies, the Expires property defaults to DateTime.MinValue—that is, the smallest possible unit of time in the .NET Framework.

Note

A server-side module that needs to write a cookie adds an HttpCookie object to the Response.Cookies collection. All cookies found on the client and associated with the requested domain are uploaded and made available for reading through the Request.Cookies collection.

Cookieless Sessions

For the session state to work, the client must be able to pass the session ID to the server application. How this happens depends on the configuration of the application. ASP.NET applications define their session-specific settings through the <sessionState> section of the configuration file. To decide about the cookie support, you set the cookieless attribute to one of the values in Table 1. The listed values belong to the HttpCookieMode enumerated type.

Table 1. HttpCookieMode Enumerated Type
Mode Description
AutoDetect Use cookies only if the requesting browser supports cookies.
UseCookies Use cookies to persist the session ID regardless of whether or not the browser supports cookies. This is the default option.
UseDeviceProfile Base the decision on the browser’s capabilities as listed in the device profile section of the configuration file.
UseUri Store the session ID in the URL regardless of whether the browser supports cookies or not. Use this option if you want to go cookieless no matter what.

When AutoDetect is used, ASP.NET queries the browser to determine whether it supports cookies. If the browser supports cookies, the session ID is stored in a cookie; otherwise, the session ID is stored in the URL. When UseDeviceProfile is set, on the other hand, the effective capabilities of the browser are not checked. For the session HTTP module to make the decision about cookies or URL, the declared capabilities of the browser are used, as they result from the SupportsRedirectWithCookie property of the HttpBrowserCapabilities object. Note that even though a browser can support cookies, a user might have disabled cookies. In this case, session state won’t work properly.

Note

In ASP.NET 1.x, you have fewer options to choose from. The cookieless attribute of the <sessionState> section can accept only a Boolean value. To disable cookies in sessions, you set the attribute to true.

With cookie support disabled, suppose that you request a page at the following URL:

http://www.contoso.com/test/sessions.aspx

What is displayed in the browser’s address bar is slightly different and now includes the session ID, as shown here:

http://www.contoso.com/test/(S(5ylg0455mrvws1uz5mmaau45))/sessions.aspx

When instantiated, the session-state module checks the value of the cookieless attribute. If the value is true, the request is redirected (HTTP 302 status code) to a modified virtual URL that includes the session ID just before the page name. When processed again, the request embeds the session ID. A special ISAPI filter—the aspnet_filter.exe component—preprocesses the request, parses the URL, and rewrites the correct URL if it incorporates a session ID. The detected session ID is also stored in an extra HTTP header, named AspFilterSessionId, and retrieved later.

Issues with Cookieless Sessions

Designed to make stateful applications also possible on a browser that does not support cookies or on one that does not have cookies enabled, cookieless sessions are not free of issues. First, they cause a redirect when the session starts and whenever the user follows an absolute URL from within an application’s page.

When cookies are used, you can clear the address bar, go to another application, and then return to the previous one and retrieve the same session values. If you do this when session cookies are disabled, the session data is lost. This feature is not problematic for postbacks, which are automatically implemented using relative URLs, but it poses a serious problem if you use links to absolute URLs. In this case, a new session will always be created. For example, the following code breaks the session:

<a runat="server" href="/test/sessions.aspx">Click</a>

Is there a way to automatically mangle absolute URLs in links and hyperlinks so that they incorporate session information? You can use the following trick, which uses the ApplyAppPathModifier method of the HttpResponse class:

<a href='<% =Response.ApplyAppPathModifier("test/page.aspx")%>' >Click</a>
 

The ApplyAppPathModifier method takes a string representing a relative URL and returns an absolute URL, which embeds session information. This trick is especially useful when you need to redirect from an HTTP page to an HTTPS page, where the full, absolute address is mandatory. Note that ApplyAppPathModifier returns the original URL if session cookies are enabled and if the path is an absolute path.

Caution

You can’t use <%…%> code blocks in server-side expressions—that is, expressions flagged with the runat=server attribute. It works in the preceding code because the <a> tag is emitted verbatim, being devoid of the runat attribute. Code blocks mentioned here have nothing to do with data binding expressions <%# … %> which are perfect legal and even desirable in server-side code. The reason why you can’t use <%…%> code blocks in server-side expressions is that the presence of the runat attribute forces the creation of a server object that is not designed for handling code blocks.

Cookieless Sessions and Security

Another issue regarding the use of cookieless sessions is connected with security. Session hijacking is one of the most popular types of attacks and consists in accessing an external system through the session ID generated for another, legitimate user. Try this: set your application to work without cookies and visit a page. Grab the URL with the session ID as it appears in the browser’s address bar, and send it immediately in an e-mail to a friend. Have your friend paste the URL in his or her own machine and click Go. Your friend will gain access to your session state as long as the session is active. The session ID is certainly not well-protected information (and probably couldn’t work otherwise). For the safety of a system, an unpredictable generator of IDs is key because it makes it difficult to guess a valid session ID. With cookieless sessions, the session ID is exposed in the address bar and visible to all. For this reason, if you are storing private or sensitive information in the session state, it is recommended that you use Secure Sockets Layer (SSL) or Transport Layer Security (TLS) to encrypt any communication between the browser and server that includes the session ID.

In addition, you should always provide users the ability to log out and call the Abandon method when they think security has been breached in this way. This contrivance reduces the amount of time available for anybody getting to use your session ID to exploit data stored in the session state. And, speaking of security, it is important that you configure the system to avoid the reuse of expired session IDs when cookieless sessions are used. This behavior is configurable in ASP.NET through the <sessionState> section, as you can read in the following section.

Configuring the Session State

The <sessionState> section has grown significantly in the transition from ASP.NET 1.x to ASP.NET 2.0. Here’s how it looks now:

<sessionState
    mode="Off|InProc|StateServer|SQLServer|Custom"
    timeout="number of minutes"
    cookieName="session cookie name"
    cookieless="http cookie mode"
    regenerateExpiredSessionId="true|false"
    sqlConnectionString="sql connection string"
    sqlCommandTimeout="number of seconds"
    allowCustomSqlDatabase="true|false"
    useHostingIdentity="true|false"
    partitionResolverType=""
    sessionIDManagerType="custom session ID generator"
    stateConnectionString="tcpip=server:port"
    stateNetworkTimeout="number of seconds"
    customProvider="custom provider name">
    <providers>
       ...
   </providers>
</sessionState>

Table 2 details goals and characteristics of the various attributes. Note that only modetimeoutstateConnectionString, and sqlConnectionString are identical to ASP.NET 1.x. The attribute cookieless also exists in ASP.NET 1.x but accepts Boolean values. All the others are supported only in ASP.NET 2.0 and newer versions.

Table 2. <sessionState> Attributes
Mode Description
allowCustomSqlDatabase If true, enables specifying a custom database table to store session data instead of using the standard ASPState.
cookieless Specifies how to communicate the session ID to clients.
cookieName Name of the cookie, if cookies are used for session Ids.
customProvider The name of the custom session state store provider to use for storing and retrieving session state data.
mode Specifies where to store session state.
partitionResolverType Indicates type and assembly of the partition resolver component to be loaded to provide connection information when session state is working in SQLServer or StateServer mode. If a partition resolver can be correctly loaded, sqlConnectionString and stateConnectionString attributes are ignored.
regenerateExpiredSessionId When a request is made with a session ID that has expired, if this attribute is true, a new session ID is generated; otherwise, the expired one is revived. The default is false.
sessionIDManagerType Null by default. If set, it indicates the component to use as the generator of session IDs.
sqlCommandTimeout Specifies the number of seconds a SQL command can be idle before it is canceled. The default is 30.
sqlConnectionString Specifies the connection string to SQL Server.
stateConnectionString Specifies the server name or address and port where session state is stored remotely.
stateNetworkTimeout Specifies the number of seconds the TCP/IP network connection between the Web server and the state server can be idle before the request is canceled. The default is 10.
timeout Specifies the number of minutes a session can be idle before it is abandoned. The default is 20.
useHostingIdentity True by default. It indicates that the ASP.NET process identity is impersonated when accessing a custom state provider or the SQLServer provider configured for integrated security.

In addition, the child <providers> section lists custom session-state store providers. ASP.NET session state is designed to enable you to easily store user session data in different sources, such as a Web server’s memory or SQL Server. A store provider is a component that manages the storage of session state information and stores it in alternative media (for example, Oracle) and layout.