Password management made easy in ASP.NET with the Crypto API
If you are building your own database of credentials then you need to store passwords. I won’t go into the details of why (but you can read them here), but the modern way of doing it is with password stretching (or iterative hashing) using the Rfc2898DeriveBytes class. This class generates a salt and then uses that to hash the password but instead of once it does it in a loop for a certain number of iterations (1000 or 10000 or whatever). This is a stronger way to store passwords compared to a single hash because it slows an attacker down if they are trying to generate a rainbow table to brute force the hashed password. If you’ve never used this class before then it can be a little confusing. Fortunately in ASP.NET (via the Crypto class in System.Web.Helpers.dll) they provide a wrapper on this and so you don’t even have to get involved with the details. Here’s how you would use this when creating a new account for a user using Crypto.HashPassword:
public void CreateAccount(string username, string password) { var hashedPassword = Crypto.HashPassword(password); CreateAccountInDatabase(username, hashedPassword); }
The beauty here is that the returned value contains both the salt and the hashed password in a single value. All you need to do is store the username and hashed password in your database.
Another consideration when doing your own password management has to do when you are validating credentials on a login page. Of course we’ll need to re-run the hashing algorithm to validate a password provided by the user. We then compare the hashed password from the user to the hashed password stored in the database.The work is also provided by the Crypto class via Crypto.VerifyHashedPassword:
public bool ValidateCredentials(string username, string password) { var hashedPassword = GetPasswordFromDatabase(username); var doesPasswordMatch = Crypto.VerifyHashedPassword(hashedPassword, password); return doesPasswordMatch; }
One subtle issue that’s not obvious here is that we don’t want to use the normal string comparison (== operator) when comparing password values. The reason is that normal string comparison will exit as soon as the first character mismatch is encountered and this can leak information to an attacker. Fortunately the implementation inside of Crypto.VerifyHashedPassword does not use the normal string comparison and always does a full character-by-character comparison to not leak this side channel information. It’s really done quite well.
So if you’re not using the membership provider then you need to manage passwords, and this can be done easily and securely with the Crypto class. Kudos to Microsoft for this API.
Can you explain more about the “information leak” when using the == operator? If I get your meaning, you are saying that you may be able to detect that the first x characters are correct when trying to do a brute force?
I guess that may be the case with some forms of cryptography, but it certainly shouldn’t be the case with hashed passwords, as changing even one character ends up with a radically different hash.
For plain text passwords, I think you see the issue. But for hashed passwords, you have a good point. I’m no mathematician but I think there is still a potential for leaks. This issue is summarized here:
http://www.hitachi.com/rd/yrl/conf/2008/indocrypt/index.html
Others disagree, though:
http://security.stackexchange.com/questions/9192/timing-attacks-on-password-hashes
This is such a simple preventative measure that I don’t think there’s any harm in incorporating it.
Though it’s probably quite similar to what’s baked in the MS API, there’s a good overall implementation of all of this (including SlowEquals) to be found here: http://crackstation.net/hashing-security.htm
Thanks for the link Daniel.
hi, use the ms implementation sha-1 per default? if so the cracking is not hard as http://www.troyhunt.com/2012/06/our-password-hashing-has-no-clothes.html states.
@humba, sorry I’m not sure what you’re asking or trying to say.
According to the documentaiton, Crypto.HashPassword only uses 1000 iterations to create the password. That was the minimum recommended value for 2000 so it is not creating hashes that you would still consider secure.
Correct. This is why I do my own password stretching in my account management library: https://github.com/brockallen/BrockAllen.MembershipReboot/blob/master/src/BrockAllen.MembershipReboot/Crypto/CryptoHelper.cs#L27
The link is not working!
Fixed, thanks.
I’m not clear on why you’d want to use an API that’s distributed with WebMatrix as opposed to the API in the .NET Framework (namely System.Security.Cryptography). I mean, I guess it’s easier to use, but isn’t there always a trade-off with investing time in MS products that might have a short shelf-life?
The API is part of the ASP.NET webstack (so it’s not solely distributed with WebMatrix). And all it does is sit on top of the crypto APIs you suggest.
The real reason to use it is because most developers are not security developers and thus probably shouldn’t be writing security code (without review by someone who knows what they’re doing). This is true no matter what programming environment you’re in.
@brockallen I did some research into this as I was working on a VB project and wanted to use the security. I stubled upon the src here: http://www.symbolsource.org/MyGet/Metadata/aspnetwebstacknightly/Project/Microsoft.AspNet.Identity.Core/2.0.0-rtm-140327/Release/Default/Microsoft.AspNet.Identity.Core/Microsoft.AspNet.Identity.Core/Crypto.cs?ImageName=Microsoft.AspNet.Identity.Core
It seem Crypto.VerifyHashedPassword() checks the byes arrays are equal, NOT the password strings.
Yes, that’s correct.
Thank you so much, exactly what I was looking for.