SCT Options - Using a database to cache SCT's in a farm, final chapter

OK, so here I am in my hotel room ready to rip off a really long blog entry to close out caching SCT’s in a database, and I can’t get to the internet. Go figure. I’ll have to wing where I left off from memory.

Last discussion I reviewed serializing a token, and left off with my first assumption proven wrong – that the token was complete when added to the cache, so I could just use this event to go ahead and also persist the token to the database. Unfortunately there is no event to hook that would key me into when the token is complete and can be persisted. An interchange with an excellent dev on the WSE team suggested overriding the security context token service to achieve the desired behavior. So while the SecurityContextTokenManager generally handles all token handling items of interest once a token is created, the SecurityContextTokenService is actually responsible for creating, populating, and issuing the SecurityContextToken. A simple override does the trick in this case:

public class DistributedCacheSCTService: SecurityContextTokenService
{
protected override RequestSecurityTokenResponse IssueSecurityToken(SecurityTokenMessage request)
{
// invoke the base implementation
RequestSecurityTokenResponse response = base.IssueSecurityToken(request);

// now filled up the SCT's properties
base.SetupIssuedToken(request, response);

// now cache the fully-fledged SCT again
ISecurityTokenManager mgr = SecurityTokenManager.GetSecurityTokenManagerByTokenType(WSTrust.TokenTypes.SecurityContextToken);
SecurityContextToken token = response.RequestedSecurityToken.SecurityToken as SecurityContextToken;

DistributedCacheSCTManager sctMgr = (DistributedCacheSCTManager)mgr;
sctMgr.PersistSecurityToken(token.Identifier,
response.RequestedSecurityToken.SecurityToken);

return response;
}
}

Essentially I’m doing the same functionality as the default implementation, only I’m also telling the manager I derived from SecurityContextTokenManager previously to persist the token once its been setup, rather then persisting when its been added to the cache. WSE also needs to be configured to use the DistributedCacheSCTService for issuing SCTs, which is achieved through configuration:

         <autoIssueSecurityContextToken enabled="true" type="SecureConvCodeService.DistributedCacheSCTService, SecureConvCodeService" />

The good news is that now I did get a complete token serialized to the cache. Bad news is when I deserialized the token, I got a new security fault. As it turns out the base token was a UsernameToken, which has a nonce value. WSE tracks the nonce values and detects reuse of a nonce, which indicates a highly probable replay attack. While I am “rehydrating” the token with the LoadTokenFromXml method on the token manager, the manager actually thinks its loading the token from a message. Since the nonce has been used before, it throws a security fault. The problem here is that I was using an artificial means to test my caching logic by not putting the token in my local in-memory cache. Typically what would have occurred is that if the manager had seen it before, the token would already be cached in memory. Otherwise it would load it back from the database, and since the replay detection also relies upon an in-memory cache of nonces, it will not cause a problem on the second service instance in the farm. In any case, I was able to circumvent this issue by one more configuration setting, this one for the UsernameTokenManager

      <securityTokenManager type="SecureConvCodeService.CustomUsernameTokenManager,SecureConvCodeService"   xmlns:wsse="https://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" qname="wsse:UsernameToken" >

           <replayDetection enabled="false"/>

       </securityTokenManager>

This did enable me to deserialize the SCT and base token. I figured I was out of the woods with my fully re-constituted token. Not so lucky. WSE ended up throwing one more exception indicating that it could not locate the security token. Now what? Well this one had me stumped, but I was fortunate enough to corner the same excellent WSE developer who helped me work through the best approach for getting around the incomplete token issue. As it ends up we discovered that there is a "feature" with the current SCT implementation that requires the token Id (which is different than the identifier) to be consistent across messages. Typically the token id should only need to be consistent within a particular message, while the SCT identifier is consistent across all messages. The simple work around was to add Id to the serialized SCT information, and to set the original value when the token is re-hydrated. Once I did this, the sample worked.

I was so excited about actually writing some code that I neglected to discuss the advantages and disadvantages of this approach. The advantages of the database cache approach are:

1) Minimizes network transport overhead (when compared to the ‘cookie’ method to be discussed next)

2) Resilient to service instance failures in the farm

3) Low performance overhead – slightly higher than using pinning, but only since local caching of tokens is still used, it’s only once per token per service instance maximum. This is a slight performance hit over the session pinning approach.

The disadvantages are:

1) Operational complexity is highest of all approaches. Database has to be maintained for caching. If a session database is already being maintained, then this could piggy back off of the existing session database.

2) Security – if the data written to the database is not encrypted, it could be compromised by someone who can sniff the network

3) Cost – database needs to be available for caching. For high availability systems, this would need to be a clustered database since an outage would take out all services that rely on SCT’s in your farm for security.

Next blog I’ll start covering the approach I prefer because it gives operational simplicity with resiliency at the cost of network and processing overhead. This uses a conceptual model near and dear to all web developers – using cookies as a means to manage session state rather than relying upon you backend services using stateful sessions. In this case though the state information is embedded within the extensibility area of the SCT itself.