MachineKey based Session Protection for WIF

When using the session facility in WIF (e.g. in ASP.NET with the SessionAuthenticationModule), the session token must be protected somehow. By default WIF uses the Windows built-in DPAPI mechanism.

While DPAPI is easy to use in single-server scenarios it has some shortcomings:

  • DPAPI requires the IIS user profile to be loaded. This can be a problem when you don’t have admin access to IIS (e.g. in hosted scenarios like Azure Web Sites [here]).
  • DPAPI only works for non load-balanced scenarios since the DPAPI key is machine specific.

One workaround was to provide a different implementation of the protection pipeline, e.g. using X.509 certificates. This wasn’t horribly hard to do but requires some custom code and setup.

In .NET 4.5 there’s a new out-of-the-box protection mechanism using the ASP.NET machine key which may be exactly the right option (for certain scenarios). But since most of you are not running .NET 4.5 – I quickly wrote an implementation of that token handler for .NET 4.0 and made it available as part of Thinktecture.IdentityModel.

To replace the built-in session token handler with the machine key based one, do this:

  1. Make sure your machine keys are synced when using a web farm.
  2. Download Thinktecture.IdentityModel (either as source code from github, or using Nuget).
  3. Replace the session handler in WIF config:

<securityTokenHandlers>

  <remove type=Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler,
Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
/>

  <add type=Thinktecture.IdentityModel.Web.MachineKeySessionSecurityTokenHandler, Thinktecture.IdentityModel />

</securityTokenHandlers>

 

HTH

This entry was posted in ASP.NET, IdentityModel. Bookmark the permalink.

36 Responses to MachineKey based Session Protection for WIF

  1. Cleber says:

    Hi! I tried to use MachineKeySessionSecurityTokenHandler in my solution but the Decode Method throws HttpExceprion (Error Validating Data). For test purpose a I’m debuging the solution on a local IIS. Maybe it work just on Azure or I’m doing something wrong?

  2. Pingback: Windows Azure and Cloud Computing Posts for 8/6/2012+ - Windows Azure Blog

  3. JMan says:

    Dominic – Without loading a certificate, is the cookie value still protected?

  4. Hi, Just a very quick note to say a huge thank you for this post!

  5. Matt Davis says:

    Fantastic, thought my STS was my silver bullet until I tried to use it on Azure.

  6. David Williams says:

    Thanks Dominick. I am sort of amazed that ACS rolled out on Azure without a solution for this.

  7. Adam Karl says:

    This was a massive improvement however there are still scenarios where we see problems de-crypting tokens. The scenarios are difficult to reproduce but the end result is that a token/cookie a user was using yesterday is no longer valid today. When this happens we see uncaught Cryptographic and/or Argument Exceptions from the MachineKeyTransform. I’ve added exception trapping around these scenarios in my implementation of the SessionAuthenticationModule and in the MachineKeyTransform. Basically, when I encounter Crypto exceptions, I log the user out (kill their cookies in my case) and send them back through the STS server to re-issue their session.

    • Hi guys, I have the exact problem as Adam Karl describe here. The thing is we can reproduce it only… say over night… when the browser remains opened, (can be easily happen using tablet or ipad). Looks like without using X.509 certificate, the only proper way to do it it to install a custom exception handling to trap those error and log user out. Just wondering, do we have an alternative? I have fixed machine key etc…

  8. @Adam – but also have expiration times – might that be the reason the token from today is expired tomorrow/invalid tomorrow?

  9. …and have a fixed machine key?

    • akarl16 says:

      Fixed machine key and the cookies are issued as session cookies so as long as the browser is open they remain. Its difficult to tell what changed in the cookie format when this happens but in my latest occurrence I also noticed that when it errored I had 2 cookies, FedAuth and FedAuth1. After I forced a re-issuance of tokens I had 3 cookies. I wouldn’t have expected this to be a problem, its just an odd coincidence that the two things occurred at the same time.

    • akarl16 says:

      Maybe we mean different things. I am using http://en.wikipedia.org/wiki/HTTP_cookie#Session_cookie which in the definition has no expiration. Is there something in the token contents that would expire? If so that would explain the problem.

  10. jIn says:

    How can I have a fixed machine key?
    Do I have to change somewhere in config? to my preferred key?

  11. Hi Dominick, thanks for the post. I’m trying to use this code but I’m receiving the following error, I cannot realize whats going on. Do you know what the problem might be. ?
    Error:
    Exception of type ‘System.ArgumentException’ was thrown.
    Parameter name: encodedData

    StackTrace:
    at System.Web.Security.MachineKey.Decode(String encodedData, MachineKeyProtection protectionOption)
    at Thinktecture.IdentityModel.Web.MachineKeyCookieTransform.Decode(Byte[] encoded) in c:\projects\ationet\development\ationet\Thinktecture.IdentityModel\Web\MachineKeyCookieTransform.cs:line 16
    at Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler.ApplyTransforms(Byte[] cookie, Boolean outbound)
    at Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler.ReadToken(XmlReader reader, SecurityTokenResolver tokenResolver)
    at Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler.ReadToken(Byte[] token, SecurityTokenResolver tokenResolver)
    at Microsoft.IdentityModel.Web.SessionAuthenticationModule.ReadSessionTokenFromCookie(Byte[] sessionCookie)
    at Microsoft.IdentityModel.Web.SessionAuthenticationModule.TryReadSessionTokenFromCookie(SessionSecurityToken& sessionToken)
    at Microsoft.IdentityModel.Web.SessionAuthenticationModule.OnAuthenticateRequest(Object sender, EventArgs eventArgs)
    at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
    at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

    • No – not sure. First time I see this. You have to debug.

      • Julien says:

        I also had the same issue…it happened when I replaced the default SessionSecurityTokenHandler with the MachineKeySessionSecurityTokenHandler and it turned out to be because the session cookies were not cleared and therefore were encrypted the old way. Just clearing the cookies fix the issue for me.
        – Julien

      • Dexter says:

        I’m getting the same error. Tried Julien’s solution below, but no luck.

      • Dexter says:

        I was able to solve my problem. My STS still had this OnServiceConfigurationCreated using RSA encryption:

        protected void OnServiceConfigurationCreated(object sender, ServiceConfigurationCreatedEventArgs e)
        {
        var sessionTransforms =
        new List(
        new CookieTransform[]
        {
        new DeflateCookieTransform(),
        new RsaEncryptionCookieTransform(
        e.ServiceConfiguration.ServiceCertificate),
        new RsaSignatureCookieTransform(
        e.ServiceConfiguration.ServiceCertificate)
        });
        var readOnlyTransforms = sessionTransforms.AsReadOnly();
        var sessionHandler = new SessionSecurityTokenHandler(readOnlyTransforms);

        e.ServiceConfiguration.SecurityTokenHandlers.AddOrReplace(sessionHandler);
        }

  12. Hi Dominick, I’m using your MachineKeySessionSecurityTokenHandler in a slightly different scenario. I’m asking my STS for a token using the CreateOnBehalfOf (where I put in a SessionSecurityToken I create myself based on the current principal). Both sides use the same machine key and your token handler. I’m seeing this InvalidArgumentException also. I use it in a “normal” scenario for the fedauth cookies and there it works fine! Is there something trivial I somehow overlooked (indeed maybe the some clock skew, will check this tomorrow) ? I’ve put your session token handler in the OnBehalfOf security token handler collection on my STS. (the main identity token is resolved by kerberos)

    Thx.
    Martin

  13. Martin says:

    Hi Dominick, thanks for your quick reply.

    I was already afraid for this answer :-)
    Any idea on how to implement it correctly (I don’t have the bootstrap token available everywhere for CreateOnBehalfOf because we are on load balanced boxes )?

    Otherwise we can set out boxes to IP affinity and use the bootstraptoken but would like to avoid it.

    Any hint or idea is welcome and thx a lot.

    Martin

    • Well – you are turning your RP into an STS. That’s not how it is supposed to work.

      You could use a different token type like SAML. Or do are redirect to get to a real token via web sso.

      • Martin says:

        Not sure what you mean with the remark of turning RP into STS, but nevertheless I did think already about the first option (going to the STS with IssuedTokenOverTransport binding instead of using that OnBehalfOf mechanism).

        I will give it a try and thanks for the hints.
        Martin

  14. cerebralminded says:

    Hi Dominick,

    We are using MachineKeySessionSecurityTokenHandler. We are trying to implement sliding sessions with a token lifetime of 30 mins. Therefore, we have the following:

    However, when we’re handling SessionAuthenticationModule_SessionSecurityTokenReceived, the lifetime is still being set to 60mins, and not 30 as we define in the config. I’ve tried a few different configurations but am having no luck. Obviously I’m doing something wrong – please can you advise?

    As a side note, we have a custom implementation of SessionSecurityTokenCache, and inside of the SessionAuthenticationModule_SessionSecurityTokenReceived handler ‘e.SessionToken.ValidTo’ is not the same as the Expires member of the TokenCacheItem we save and then retreive from cache (we’re set TokenCacheItem.Expires to the ‘expiryTime’ value coming into our AddOrUpdate override in our custom SessionSecurityTokenCache). Why the mismatch? What is the purpose of the expiryTime parameter in AddOrUpdate?

    Thanks,

  15. Boniface says:

    Hi Dominick
    Thanks for your post. However I have a few querries. I have my site hosted on Windows Azure and authenticated using Windows Azure Active Directory. However when i run the site I am receiving the following error “The data protection operation was unsuccessful. This may have been caused by not having the user profile loaded for the current thread’s user context, which may be the case when the thread is impersonating.”.
    I have imported “Thinktecture” library into my project. In your tutorial you have explained about changing WIF config. Do I change the main projects config file, or where do i add those configuartion settings, since theres no file named WIF config.

    When I include the configuartions settings in the main project config file, i receive an error ” The configuration section ‘securityTokenHandlers’ cannot be read because it is missing a section declaration””.

    Kindly please help

  16. larsnikolajsen says:

    Hi Dominick

    I have been using the Thinktecture IdentityServer in production with WsFederation for more than a year now. I haven’t added a specific machinekey to the applications web.config file. I now have the issue that I want to move my production webserver to some new hardware, but when I do so (and spoofing a dns redirect in the hosts file), I get an encryption error for existing users which results in a blank screen in my browser. Is there any way to export the autogenerated machinekey from the old application on the old server to the new server, and thereby make a smooth transition for all the users with an exisitng access token?

    Thanks

Leave a reply to Sebastian Renzi Cancel reply