Update: I should have mentioned this when I first posted, but some of these thoughts are the result of me reading Programming Windows Identity Foundation. While I hope I haven’t copied the ideas outright, I believe the interpretation is unique-ish.
One of the main reasons we as developers shy away from new technologies is because we are afraid of it. As we learned in elementary school, the reason we are afraid usually boils down to not having enough information about the topic. I’ve found this especially true with anything security related. So, lets think about something for a minute.
I’m not entirely sure how valid a method this is for measure, but I like to think that as developers we measure our understanding of something by how much we abstract away the problems it creates. Now let me ask you this question:
How much of an abstraction layer do we create for identity?
Arguably very little because in most cases we half-ass it.
I say this knowing full well I’m extremely guilty of it. Sure, I’d create a User class and populate with application specific data, but to populate the object I would call Active Directory or SQL directly. That created a tightly coupled dependency between the application and the user store. That works perfectly up until you need to migrate those users in a SQL database to Active Directory. Oops.
So why do we do this?
My reason for doing this is pretty simple. I didn’t know any better. The reason I didn’t know better was also pretty simple. Of the available options to abstract away the identity I didn’t understand how the technology worked, or more likely, I didn’t trust it. Claims based authentication is a perfect example of this. I thought to myself when I first came across this: “are you nuts? You want me to hand over authentication to someone else and then I have to trust them that what they give me is valid? I don’t think so.”
Well, yes actually.
Authentication, identification, and authorization are simply processes in the grand scheme of an application lifecycle. They are privileged, but that just means we need to be careful about it. Fear, as it turns out, is the number one reason why we don’t abstract this part out.*
With that, I thought it would be a perfect opportunity to take a look at a few of the reasons why Claims based authentication is reasonably secure. I would also like to take this time to compare some of these reasons to why our current methods of user authentication are usually done wrong.
First and foremost we trust the source. Obviously a bank isn’t going to accept a handwritten piece of paper with my name on it as proof that I am me. It stands to reason that you aren’t going to accept an identity from some random 3rd party provider for important proof of identity.
Encryption + SSL
The connection between RP and STS is over SSL. Therefore no man in the middle attacks. Then you encrypt the token. Much like the SSL connection, the STS encrypts the payload with the RP’s public key, which only the RP can decrypt with its private key. If you don’t use SSL anyone eavesdropping on the connection still can’t read the payload. Also, the STS usually keeps a local copy of the certificate for token encryption.
How many of us encrypt our SQL connections when verifying the user’s password? How many of us use secured LDAP queries to Active Directory? How many of us encrypt our web services? I usually forget to.
Most commercial STS applications require that each request come from an approved Relying Party. Moreover, most of those applications require that the endpoint that it responds to also be on an approved list. You could probably fake it through DNS poisoning, but the certificates used for encryption and SSL would prevent you from doing anything meaningful since you couldn’t decrypt the token.
Do we verify the identity of the application requesting information from the SQL database? Not usually the application. However, we could do it via Kerberos impersonation. E.g. lock down the specific data to the currently logged in/impersonated user.
Expiration and Duplication Prevention
All tokens have authentication timestamps. They also normally have expiration timestamps. Therefore they have a window of time that defines how long they are valid. It is up to the application accepting the token to make sure the window is still acceptable, but it is still an opportunity for verification. This also gives us the opportunity to prevent replay attacks. All we have to do is keep track of all incoming tokens within the valid time window and see if the tokens repeat. If so, we reject them.
There isn’t much we can do in a traditional setting to prevent this from happening. If someone eavesdrops on the connection and grabs the username/password between the browser and your application, game over. They don’t need to spoof anything. They have the credentials. SSL can fix this problem pretty easily though.
Once the token has been created by the STS, it will be signed by the STS’s private key. If the token is modified in any way the signature wont match. Since it is being signed by the private key of the STS, only the STS can resign it, however anyone can verify the signature through the STS’s public key. And since it’s a certificate for the STS, we can use it as strong proof that the STS is who they say they are. For a good primer on public key/private key stuff check out Wikipedia.
It's pretty tricky to modify payloads between SQL and an application, but it is certainly possible. Since we don’t usually encrypt the connections (I am guilty of this daily – It’s something I need to work on ), intercepting packets and modifying them on the fly is possible. There isn’t really a way to verify if the payload has been tampered with.
Sure, there is a level of trust between the data source and the application if they are both within the same datacenter, but what if it’s being hosted offsite by a 3rd party? There is always going to be a situation where integrity can become an issue. The question at that point then is: how much do you trust the source, as well as the connection to the source?
Finally, if we are willing to accept that each item above increases the security and validity of the identity, there is really only one thing left to make sure is acceptable. How was the user authenticated? Username/password, Kerberos, smart card/certificates, etc. If we aren’t happy with how they were authenticated, we don’t accept the token.
So now that we have a pretty strong basis for what makes the tokens containing claims as well as the relationship between the RP’s and STS’s secure, we don’t really need to fear the Claims model.
Now we just need to figure out how to replace our old code with the identity abstraction.
* Strictly anecdotal evidence, mind you.