Windows Identity Foundation not removing Session Security Token from Cache

Recently were I work we had a PEN test on one of our applications and that highlighted that the federated authentication token for a relying party wasn’t being cleaned up correctly when logging out. After sitting down and working out what was cause for this problem I thought I would share my findings and the solution.

The problem

At work we use a custom session security token cache to store the token so we don’t have to pass around so much data to and from the client. When logging out from a replying party using a federated sign out e.g. “wsignoutcleanup1.0” the WIF code base should use the federation authentication module (FAM) and the “SignOut” method to get the session authentication module (SAM) and tidy up/delete the current session token. See the code example 1 below.

Example 1

public virtual void SignOut(bool isIPRequest)
{
  try
  {
    this.OnSigningOut(new SigningOutEventArgs(isIPRequest));
    SessionAuthenticationModule.Current.DeleteSessionTokenCookie();
    this.OnSignedOut(EventArgs.Empty);
  }
} 

The session authentication module and its method “DeleteSessiontokenCookie” then checks that the property “ContextSessionSecurityToken” isn’t null before calling the actual code to remove the session security token from the persistent cache and the in memory one. See example 2 below.

Example 2

public void DeleteSessionTokenCookie()
{
  this.CookieHandler.Delete();
  if (this.ContextSessionSecurityToken == null)
    return;
  this.RemoveSessionTokenFromCache(this.ContextSessionSecurityToken);
}

Now debugging the code and decompiling it I was able to work out that the session authentication module sets the “ContextSessionSecurityToken” when the “OnAuthenticateRequest” event is handled in the SAM. However, when signing out from the RP using “wsignoutcleanup1.0” this event is not fired or handled by the SAM if the FAM is defined above it in the web.config modules section (see below). No you read that correctly the order of the FAM and SAM in the web config appear to determine whether your token is cleared from the cache or not!

Does not remove the item from the cache

<removename="FormsAuthentication" />

<add name="WSFederationAuthenticationModule"
  type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
  preCondition="managedHandler" />

<add name="SessionAuthenticationModule" 
  type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
  preCondition="managedHandler" />

The fix

Now, I’m not certain but I do not believe an application should be dependent on the order of HTTP modules to function correctly. Am I missing something? A quick fix for us is indeed to switch the order of the modules in the web.config to get the “ContextSessionSecurityToken” to be set (See example modules below).

<remove name="FormsAuthentication" />

<add name="SessionAuthenticationModule" 
  type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" 
  preCondition="managedHandler" />

<add name="WSFederationAuthenticationModule" 
  type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" 
  preCondition="managedHandler" />

Additionally I can also add some code to the FAM to do manually set the property on the SAM (see below). Both approaches feel wrong to me in that they fix an issue in the underline WIF implementation. Well potential issue…

FAM change

public override void SignOut(bool isIPRequest)
{
  var sessionModule = FederatedAuthentication.SessionAuthenticationModule;
  SessionSecurityToken token;
  var hasManagedToRead = sessionModule.TryReadSessionTokenFromCookie(out token);
  if (hasManagedToRead)
  {
    var lafSam = sessionModule asLafSessionAuthenticationModule;
    lafSam.ContextSessionSecurityToken = token;
  }
  sessionModule.DeleteSessionTokenCookie();
  base.SignOut(isIPRequest);
}

SAM change (redefined property in the LAF custom SAM so I could set the property!

public SessionSecurityToken ContextSessionSecurityToken
{
  get
  {
    return (SessionSecurityToken)HttpContext.Current.Items[(object)typeof(SessionSecurityToken).AssemblyQualifiedName];
  }
  set
  {
    HttpContext.Current.Items[(object)typeof(SessionSecurityToken).AssemblyQualifiedName] = (object)value;
  }
}

Until next time.

May 13 2014
blog comments powered by Disqus