NullReferenceException and MachineKey.Decode


In my recent project I had to sign a http cookie in order to disallow any unauthorized changes to its content. I didn’t want to reinvent the wheel but use something already implemented in ASP.NET – for instance mechanism that is used to sign ViewState content. After some research I found promising methods: System.Web.Security.MachineKey.Encode/Decode (I’m using .NET4, in 4.5 these method are obsolete and new methods: Protect/Unprotect were introduced to replace them). Let’s first look at an example how to use those methods. The below code snippet retrieves content of a signed cookie or prints information that the cookie was tampered:

<%@ Application Language="C#" %>
<%@ Import Namespace="System.Web.Security" %>
<%@ Import Namespace="System.Web.Configuration" %>

<script Language="c#" runat="server">

    void Application_Start(Object sender, EventArgs ev) {
    }

    void Application_BeginRequest(Object sender, EventArgs ev)
    {
        try {
            var cookie = Request.Cookies["_sec"];
            if (cookie != null && !String.IsNullOrEmpty(cookie.Value)) {
                var txt = MachineKey.Decode(cookie.Value, MachineKeyProtection.Validation);
                if (txt == null) {
                    Response.Write("Cookie tampered.");
                } else {
                    Response.Write(Encoding.ASCII.GetString(txt));
                }
            }
        } catch (Exception ex) {
            Response.Write("Exception: " + ex);
        }
    }

    void Application_EndRequest(Object sender, EventArgs ev)
    {
        var signedtxt = MachineKey.Encode(Encoding.ASCII.GetBytes("test string"), MachineKeyProtection.Validation);
        Response.SetCookie(new HttpCookie("_sec", signedtxt));
    }

</script>

With default settings the key, used to sign the cookie, is randomly created by ASP.NET and algorithm to generate the hash is HMACSHA256. You may alter those settings by modifying machineKey section in the web.config file (more on MSDN).

What was a surprise to me after I ran this code was the fact that the first request to my application (with the cookie set) generated NullReferenceException which pointed to the MachineKey.Decode method. I ran the debugger, opened referencesource-beta.microsoft.com and by comparing generated assembly with the original C# code I found a line to blame (highlighted):

    //////////////////////////////////////////////////////////////////
    // Step 3a: Remove the hash from the end of the data
    if (data.Length < MachineKeySection.HashSize)
        return null;

MachineKeySection.HashSize blindly assumes that it is already initialized and tries to retrieve the hashsize from the configuration object (which is null at this point):

    internal static int HashSize { get { s_config.RuntimeDataInitialize(); return _HashSize; } }

Subsequent requests run fine as MachineKey.Encode method initializes s_config properly. But if application reboots the first request will again be “exceptional” 🙂

I already submitted a bug report on Microsoft connect (https://connect.microsoft.com/VisualStudio/feedback/details/827886/nullreferenceexception-when-machinekey-decode-is-called) so if you happen to encounter this error please upvote my report. For now, as a simple workaround I recommend calling MachineKeySection.EnsureConfig method when your application starts (it is internal so we must use reflection), eg.:

    void Application_Start(Object sender, EventArgs ev) {
        typeof (MachineKeySection).GetMethod("EnsureConfig";, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).Invoke(null, null);
    }

A simple ASP.NET application that demonstrates this problem can be found on my blog samples page.

NullReferenceException and MachineKey.Decode

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s