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 (15x8 =
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 mode, timeout, stateConnectionString, 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.