As another example, suppose that a user opens a browser on a public kiosk, logs on to a bank account using the bank’s Web site, logs off and leaves, but does not close the browser. The response to the user’s logon page, which contains the STS token, is still in the browser’s history. Another person could then browse back to that response page and replay it, which would repost the STS token to the bank’s Web site.This scenario is very much real and it does not involve any fancy hacking techniques. All you need is a browser and a "back" button. You'll find some scattered references on the Internet to the solution of the problem, the tokenReplayDetection configuration setting. You'll find a mention of the configuration element in the WIF FAQ on Technet and in the WIF book, but you'll find the most helpful explanation in the ACS security guidelines.
I'll cut to the chase, here's the config to enable the token replay detection. Please don't use the parameters as is, read the security considerations and tweak the values accordingly. Seriously.
<microsoft.identityModel> <service> <tokenReplayDetection enabled="true" capacity="1000" expirationPeriod="00:10:00" /> </service> </microsoft.identityModel>
Note that the expirationPeriod attribute expects a TimeSpan. See the TimeSpan.Parse method for examples on how to set the value. You might be surprised to see that e.g. a value of "10" would make it ten days, and not 10 minutes. My example would make it ten minutes.
The replay detection is based on a cache that keeps track of security tokens already seen. The "Replay detection" article explains more about how it works:
This cache does not guarantee that a token can never be replayed. It performs best effort detection based on the size of the cache, the expiry time of the STS token, and the rate of unique authentication requests received by the RP. It is strongly recommended that you tune the cache size and STS token expiry time for your RP to get the right balance between performance and security.First and foremost, the expirationPeriod must be longer than the period the security tokens are valid. Security tokens should not expire from cache before they have expired themselves. If a token is valid for five minutes, you would want to cache it for at least six minutes. After six minutes it would not be valid anymore, hence it cannot be replayed and can be safely removed from cache.
You need to estimate how many tokens you would need to keep in the cache for the duration of the expirationPeriod. This value is highly dependent on the traffic on your site. You should of course base this estimate on the peak hours of the site and set the capacity attribute accordingly.
Finally, there's the issue of server affinity. Your now fine tuned replay detection cache is not shared between servers. That means that you need to have sticky sessions if you're running a web farm — to ensure that a client keeps hitting the same server. If the user hits a new server, the security token will not be found in the cache.
Verifying replay detection
It's important to verify that the replay detection works. To do that you could log out of your application. Then go back to the page where the security token was submitted and trigger the browser to "resubmit the form." Another approach would be to use a tool such as Fiddler to replay the web request where your security token is posted to WIF. When WIF detects a replayed token it will throw the following exception:
ID1062: Replay has been detected for: Token: 'System.IdentityModel.Tokens.SamlSecurityToken', AssertionId: '_b3f7608b-9c6f-4efc-8300-3e8373f62df3', Issuer: 'Name of STS'.Don't assume that replay detection works, verify that a replay triggers the exception!
Remaining security risks
Unfortunately, even if you do everything right, things can still go wrong in the default WIF setup.
- If a server is removed from cluster, all tokens that are cached on that server (but not expired) can be replayed. They'd hit another server which would accept the token, sign in the user and put the security token in its cache.
- If the capacity of the cache is exceeded, valid tokens should be expected to be purged from the cache. Hence, they could be replayed to the very same server. So choose the capacity wisely!
You would have to implement your own SecurityTokenCache, backed by e.g. AppFabric or a SQL server, to share the security token cache between servers. Hopefully there will be built-in providers in the not so distant future, now that WIF will be incorporated in the .NET framework.
There, hope that helps!
PS! It seems the configuration might be changing for .NET 4.5 where WIF will be included in the System.IdentityModel namespace. According to the 4.5 documentation, there's no capacity attribute.