Summary
In this blog post I’m going to talk about the ID manager used by the .Net HttpContext.Current.Session and how to customize the ID that is generated to create a more secure ID.
Session ID
The session ID that is created when you use the Session object is contained in a cookie named ASP.NET_SessionId by default. You can override the cookie name in the web.config file by using the following syntax:
<configuration> <system.web> <sessionState cookieName="MySessionID" /> </system.web> </configuration>
This will cause the cookie to be named “MySessionID”. You can also set the session to become cookieless. I’m not go into cookieless sessions in this blog post. What I am going to talk about is how to override the mechanism that generates the actual session id stored inside the cookie. In a future blog post I’ll take this further and discuss how the cookie generation code can be modified to give you more control over this process.
The default .Net algorithm that is used to generate the id uses lower case characters ‘a’ thorugh ‘z’ and the numbers ‘0’ through ‘5’. The reason for this sequence is that they are safe to use in a URL if you switch to cookieless. The choice of only 6 numbers is due to the fact that the total characters to choose from becomes 32, which is an even binary number. That allows the random number generator used to choose the letter for each digit to be un-biased. For an excellent article about random number biasing when using the mod function I’ll refer you to this blog post: How much bias is introduced by the remainder technique?
The SessionIDManager Object
The SessionIDManager object is what performs the id generation and I’m going to show how to override the CreateSessionID() and Validate() methods in order to generate your own session id. Microsoft has sample code here: SessionIDManager.CreateSessionID Method (HttpContext) using a GUID for the id. DON’T DO IT THIS WAY! This is a very simple and quick way to generate a custom id, but it’s also relatively easy to hack. The problem with the GUID is that it is unique, but it is not random. It’s pseudo-random and predictable. If you refer to RFC4122, section 6:
Do not assume that UUIDs are hard to guess; they should not be used as security capabilities (identifiers whose mere possession grants access), for example. A predictable random number source will exacerbate the situation.
Make sure you use a cryptographically random number generator. For this we can use the RNGCryptoServiceProvider object. In addition to this, I’m going to make the key length 64 characters (you can go up to 80). Here’s the C# code in total:
using System.Web; using System.Web.SessionState; using System.Security.Cryptography; using System.Linq; namespace MyCustomSession { public class CustomSessionIDManager : SessionIDManager { public const int KEY_LENGTH = 64; private char[] Encoding = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5' }; public override string CreateSessionID(HttpContext context) { char[] identifier = new char[KEY_LENGTH]; byte[] randomData = new byte[KEY_LENGTH]; using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider()) { rng.GetBytes(randomData); } for (int i = 0; i < identifier.Length; i++) { int pos = randomData[i] % Encoding.Length; identifier[i] = Encoding[pos]; } return new string(identifier); } public override bool Validate(string id) { try { if (id.Length != KEY_LENGTH) { return false; } for (int i=0; i < id.Length; i++) { if (!Encoding.Contains(id[i])) { return false; } } return true; } catch { } return false; } } }
And you’ll need to modify your web.config to match:
<configuration> <system.web> <sessionState cookieName="MySessionID" sessionIDManagerType="MyCustomSession.CustomSessionIDManager, MyCustomSession" /> </system.web> </configuration>
You’ll notice that MyCustomSession is used twice in the sessionIDManagerType definition. The first one is the namespace and the name after the comma is the name of the dll.
I used the same character encoding that Microsoft uses, but you can increase the charactors to increase your security. Just make sure you use an even binary number like 64 characters. Also be aware of characters that might be difficult to work with (like the period, commas, back-slash, less-than, greater-than, etc.). If you use upper case letters, lower case letters and all numbers you’ll end up two characters short of 64 characters. So you’re forced to use two non-number/non-letter characters.
The next thing to note about the above class is that there is a validate() method. This opens up some possibilities for a custom sequence that you can use to prevent spoofing. In such a case I would try and keep such information classified so an outsider has difficulty in spoofing your id. In the sample above, I’m only checking to make sure that the length is exactly correct and that each character in the id is in the list of valid characters.
Where to Get the Code
I have posted this example on my GitHub account and you can download it by clicking here. This was written in Visual Studio 2015, but you can use it in earlier versions of Visual Studio.