Skip to content

Use the MachineKey API to protect values in ASP.NET

June 21, 2012

It’s quite common to need to preserve state across requests in a web application. This is typically in the form of a cookie, query string or hidden form field. Commonly the state that needs to be sent back to the client is sensitive or we want to ensure it’s not been modified by the user. The typical approach to this should be encrypting (protect) and MACing (verify) the value. Writing this sort of crypto code yourself is possible but not ideal. Fortunately in ASP.NET the MachineKey API already provides this functionality. And yes, this is the same as the <machineKey> infrastructure already used to protect Forms authentication and ViewState.

The API is slightly different between 4.0 and 4.5. Here’s the 4.0 usage with some helpers:

string Protect(byte[] data)
{
    if (data == null || data.Length == 0) return null;
    return MachineKey.Encode(data, MachineKeyProtection.All);
}

byte[] Unprotect(string value)
{
    if (String.IsNullOrWhiteSpace(value)) return null;
    return MachineKey.Decode(value, MachineKeyProtection.All);
}

MachineKey.Encode accepts a byte[] to protect and returns a string. The second parameter is an enum that indicates if you want encryption, validation or both. I’d typically suggest both (MachineKeyProtection.All). The returned string can then be used to pass back to the client as a cookie value or a query string value without concern for viewing or tampering. MachineKey.Decode simply reverses the process.

And here’s the 4.5 usage (it supports a slightly more sophisticated usage):

const string MachineKeyPurpose = "MyApp:Username:{0}";
const string Anonymous = "<anonymous>";

string GetMachineKeyPurpose(IPrincipal user)
{
    return String.Format(MachineKeyPurpose,
        user.Identity.IsAuthenticated ? user.Identity.Name : Anonymous);
}

string Protect(byte[] data)
{
    if (data == null || data.Length == 0) return null;
    var purpose = GetMachineKeyPurpose(Thread.CurrentPrincipal);
    var value = MachineKey.Protect(data, purpose);
    return Convert.ToBase64String(value);
}

byte[] Unprotect(string value)
{
    if (String.IsNullOrWhiteSpace(value)) return null;
    var purpose = GetMachineKeyPurpose(Thread.CurrentPrincipal);
    var bytes = Convert.FromBase64String(value);
    return MachineKey.Unprotect(bytes, purpose);
}

In 4.5 the old APIs are deprecated in favor of these new Protect and Unprotect APIs. The new APIs no longer accept the level of protection (they always encrypt and MAC now [which is good]) and instead now accept a new parameter which is called “purpose”. This purpose parameter is intended to act somewhat as a validation mechanism. If we use a value that’s specific to the user (as we do above with the GetMachineKeyPurpose helper) we then are verifying that the value can only be unprotected by the same user. This is a nice addition in 4.5.

In my Cookie-based TempData provider I used this exact technique — I didn’t want to reinvent crypto or introduce new keys, so leveraging the ASP.NET machine key APIs made a lot of sense.

Oh and one caveat: this does not prevent an eavesdropper from intercepting the value and replaying it, so (as usual) SSL is imperative.

Update: Great follow-up reading about the internals of the MachineKey:

11 Comments leave one →
  1. meenu permalink
    June 28, 2012 2:39 am

    Can u help me to write this functions in vs2010 silverlight 4 application.This machine key retriving error in my vs2010 web project

    • June 28, 2012 10:27 am

      @meenu: These MachineKey functions are intended to be used from the server, not on the client.

  2. December 16, 2012 12:55 pm

    Hallo Brock! It seems that those new method have issues ecnrypting cookie values since converting the encoded byte flush to string creates a string not suitable for cookie value and thous generating exception on Unprotect of that string reconverted to byte()

    Any hint on how to use those methods for cookie protection? Or any other new class instead of the deprecated encode/decode methods?

  3. Pottify permalink
    June 3, 2015 12:00 pm

    Great post Brock.
    Can I use this approach to decrypt the session cookie created by SessionAuthenticationModule, and if so, what is the format of the ‘purpose’ parameter?

    I need to do this because I have an OWIN app, hosted on the same domain as my ASP.NET app, and I want to share session between the two. Therefore, I planned to simply manipulate the SAM cookie in the OWIN app and know that the ASP.NET app can still access it. Or perhaps there is a better approach?

    • June 5, 2015 8:02 pm

      I don’t what they use for session. You’d have to decompile the code.

  4. November 18, 2015 9:41 am

    I am facing an issue where MachineKey encrypted value doesn’t survive app pool restart. You encrypt something and then upon pool recycle its not able to decrypt. Any idea what would be the cause?

  5. Mazhar permalink
    November 18, 2015 9:43 am

    I am facing an issue with app pool restart. It seems like every time app pool recycles MachineKey is not able to decrypt values encrypted before restart. Any idea what would be causing this or its even expected.

    • January 2, 2016 11:29 am

      You need to set an explicit value in web.config to be sure it won’t change.

Trackbacks

  1. Cryptographic Improvements in ASP.NET 4.5, pt. 3 - .NET Web Development and Tools Blog - Site Home - MSDN Blogs

Leave a comment