Implementing User Sessions in ASP.NET without SessionState

Few things are more disappointing to an ASP.NET programmer than this one line of code in a web.config:

<sessionState mode="Off" />

You probably know what I mean if you’ve ever been asked to implement user sessions for a web site that has ASP.NET’s built-in session state disabled.  In this post I will offer one simple alternative that allows you to implement user sessions on just such a web site.

Cookies Taste Better In-Memory

The secret to my solution is HTTP cookies.  In my experience, programmers are increasingly afraid to create sites that require cookies because of the proliferation of cookie-blocking browsers and add-ins.  However, there are two different kinds of cookies—session cookies and persistent cookies—and they are treated very different by cookie blockers.  Session cookies have no expiration date specified by the server.  This causes browsers to stores them in memory only.  When the user closes the browser window (browser process, technically) all session cookies--which reside in memory, not on disk--are gone.  This is much less of a security/privacy risk, as so typically, session cookies are not blocked.  In fact, ASP.NET uses session cookies by default to relate a user to a session on the server.

Cookies can also be first-party or third-party   Third-party cookies are ones received from a domain other than the domain of the web page.  These cookies are often blocked because they are used by advertisers and marketers to track users across different web sites.

Since this solution relies on first-party session cookies, it shouldn’t run into any serious cookie-blocking issues.

Create the Cookie

Before creating a new cookie, don’t forget that it may already exists.  You can check to see if the cookie name you intend to use already exists, and if it does, use the existing one.  Otherwise you’ll need to create a new cookie.  Either way you can then set the values you want to set on that cookie (in this case, userid) and put the modified cookie into the response.

HttpCookie cookie = Request.Cookies[MYSITECOOKIENAME];
cookie = cookie ?? new HttpCookie(MYSITECOOKIENAME);
cookie.Values.Set("userid", user.UserID.ToString());
Response.SetCookie(cookie);

Note that there is no expiration date set in the code above.  This otherwise overlookable fact is actually quite important, because this is how this cookie becomes a session cookie instead of a persistent cookie.

Check the Cookie

On all future requests to secure pages, we can now read the cookie sent with the request to see if the userid is in it.  If not, or if the cookie is missing altogether, then the user must not be logged in.

HttpCookie cookie = Request.Cookies[MYSITECOOKIENAME];
if (cookie != null && cookie.Values["userid"] != null && cookie.Values["hash"] != null)
{
    return cookie.Values["userid"].Length > 0;
}
else
{
    LogOutUser();
    return false;
}

In the code above, I’m verifying the user by simply testing whether the userid exists in the cookie.  Alternately I could have looked it up in the database, but  in the next section I’m going to show a different alternative that doesn’t require hitting the database again.

Secure the Cookie

Now that we can write a session cookie and read it on subsequent requests, we need to make sure that no one can, with some simple hacking, masquerade as someone else on our website.  We can achieve this by hashing the userid plus a salt and putting that in the cookie.  Then, when reading the cookie on future requests, we can rehash the userid-plus-salt and compare it to the one in the cookie.  If it matches, we know this cookie was created by our code.

Here’s my simple implementation of the SHA512 hashing algorithm provided by the .NET Framework:

private static string GenerateHash(string value, string salt)
{
    byte[] buffer = UnicodeEncoding.UTF8.GetBytes(value + ":" + salt);
    SHA512CryptoServiceProvider cryptoTransformSHA512 = new SHA512CryptoServiceProvider();
    return BitConverter.ToString(cryptoTransformSHA512.ComputeHash(buffer));
}

Note that the salt is necessary because the SHA512 algorithm is a well known hashing algorithm.  Without a salt, a savvy hacker could theoretically hack my site by generating their own valid SHA512 hash of the userid they want in their cookie.

The hash of our userid-plus-salt string can now be stored in the cookie alongside the userid, as shown in this code:

HttpCookie cookie = Request.Cookies[MYSITECOOKIENAME];
cookie = cookie ?? new HttpCookie(MYSITECOOKIENAME);
cookie.Values.Set("userid", user.UserID.ToString());
cookie.Values.Set("hash", GenerateHash(user.UserID.ToString(), SALT));
Response.SetCookie(cookie);
return true;

Finally, when attempting to verify a user’s session, we can simply re-hash the userid sent in the request and see if that hash matches the one sent in the same request.

HttpCookie cookie = Request.Cookies[MYSITECOOKIENAME];
if (cookie != null && cookie.Values["userid"] != null && cookie.Values["hash"] != null)
{
    return (cookie.Values["hash"] == GenerateHash(cookie.Values["userid"], SALT));
}
else
{
    LogOutUser();
    return false;
}

You can download a working sample I created to see this in action.

comments powered by Disqus