How and why session IDs are reused in ASP.NET
The ASP.NET session state is a technology that lets you store server-side, user-specific data. Web applications can use this data to process requests from the user for which the session state was instantiated. A session state user is identified by a session ID. The session ID is delivered by using one of the following methods: The session ID is part of a cookie that is sent to the browser of the user.The session ID is embedded in the URL. This technique is also known as a cookie-less session.Session IDs are a 120-bit random number that is represented by a 20-character string. The string is formatted so that it can be included in a URL and it does not have to undergo URL encoding. For example, the string may be used in cookie-less sessions. The most commonly used method of delivering session IDs is by using cookies to store the session IDs.
When a user first opens their Web browser and then goes to a Web site
that implements ASP.NET session state, a cookie is sent to the browser with the
name "ASP.NET_SessionId" and a 20-character value.
When the user browses within the same DNS domain, the Web browser continues to send this cookie to the domain for which it was sourced.
For example, app1.tailspintoys.com and app2.tailspintoys.com are both ASP.NET applications. If the user goes to app1.tailspintoys.com and then
goes to app2.tailspintoys.com, both applications would use the
same cookie and the same session ID to track the session state of the user within each
application. The applications do not share the same session state. The applications only share the session ID.
Therefore, you can reuse session IDs for
several reasons. For example, if you reuse session IDs, you do not have to do the following:
Create a new cryptographically
unique session ID when you are presented with a valid session ID.Create a new session ID for every
ASP.NET application that is in a single domain. When the Web application requires a logon and offers a log off page or option, we recommend that you clear the session state when the user has logged off the Web site. To clear the session state, call the Session.Abandon method. The Session.Abandon method lets you flush the
session state without waiting for the session state time-out. By default, this
time-out is a 20-minute sliding expiration. This expiration is refreshed every time that the
user makes a request to the Web site and presents the session ID cookie. The Abandon method sets a flag in the session state object that indicates that the
session state should be abandoned. The flag is examined and then acted upon at the
end of the page request. Therefore, the user can use session objects within the page
after you call the Abandon method. As soon as the page processing is completed,
the session is removed.
When you use the in-process session state mode, these session state objects are stored in the HttpCache. The HttpCache supports a callback method when the following conditions are true: A cache entry is removed.The Session State Manager
registers the Session_OnEnd event handler to be called when the cache entry is
removed. When the Session State Manager removes a session state object that
resides in the cache, the HttpCache manager will call any registered callbacks. In effect, this behavior raises the Session_OnEnd event handler.
When you abandon a
session, the session ID cookie is not removed from the browser of the user. Therefore, as soon as the session has been
abandoned, any new requests to the same application will use the same session ID but will have a new session state
instance. At the same time, if the user opens another application within the same DNS
domain, the user will not lose their session state after the Abandon method is called from one application.
Sometimes, you may not want to reuse the session ID. If you do and if you understand the ramifications of not reusing the session ID, use the following code example to abandon a session and to clear the session ID cookie:
Session.Abandon();
Response.Cookies.Add(new HttpCookie("ASP.NET_SessionId", ""));
This code example clears the session state from the server and sets
the session state cookie to null. The null value effectively clears the cookie from the browser.
When a user does not log off from the application and the session state time-out occurs, the application may still use the same session state
cookie if the browser is not closed. This behavior causes the user to be directed to
the logon page and the session state cookie of the user to be presented. To guarantee
that a new session ID is used when you open the logon page (login.aspx), send a null cookie back to the client. To do this, add a cookie to
the response collection. Then, send the response collection back to the client. The easiest way to
send a null cookie is by using the Response.Redirect method. Because the cookies collection always has a value for the ASP.NET_SessionId, you cannot just test if this cookie exists because you will create a Response.Redirect loop. You can set a query string on the redirect to the logon page.
Or, as illustrated in the following code example, you can use a different cookie to tell if you are already redirected to the logon page. To help enhance security and to make sure that no one tries to open the logon page by using a second cookie together with the ASP.NET cookie, the following code example uses the FormsAuthentication class to encrypt and decrypt the cookie data. Then, the code example sets a 5-second time-out.
private void Page_Load(object sender, System.EventArgs e)
{
if( !IsPostBack &&
( Request.Cookies["__LOGINCOOKIE__"] == null ||
Request.Cookies["__LOGINCOOKIE__"].Value == "" ) )
{
//At this point, we do not know if the session ID that we have is a new
//session ID or if the session ID was passed by the client.
//Update the session ID.
Session.Abandon();
Response.Cookies.Add(new HttpCookie("ASP.NET_SessionId", ""));
//To make sure that the client clears the session ID cookie, respond to the client to tell
//it that we have responded. To do this, set another cookie.
AddRedirCookie();
Response.Redirect( Request.Path );
}
//Make sure that someone is not trying to spoof.
try
{
FormsAuthenticationTicket ticket =
FormsAuthentication.Decrypt( Request.Cookies["__LOGINCOOKIE__"].Value );
if( ticket == null || ticket.Expired == true )
throw new Exception();
RemoveRedirCookie();
}
catch
{
//If someone is trying to spoof, do it again.
AddRedirCookie();
Response.Redirect( Request.Path );
}
Response.Write("Session.SessionID="+Session.SessionID+"<br/>");
Response.Write("Cookie ASP.NET_SessionId="+Request.Cookies["ASP.NET_SessionId"].Value+"<br/>");
}
private void RemoveRedirCookie()
{
Response.Cookies.Add(new HttpCookie("__LOGINCOOKIE__", ""));
}
private void AddRedirCookie()
{
FormsAuthenticationTicket ticket =
new FormsAuthenticationTicket(1,"Test",DateTime.Now,DateTime.Now.AddSeconds(5), false,"");
string encryptedText = FormsAuthentication.Encrypt( ticket );
Response.Cookies.Add( new HttpCookie( "__LOGINCOOKIE__", encryptedText ) );
}