8 ms·
Salted Password Hashing - Doing it Right
- crazygringo 14y ago> Every time a user creates an account or changes their password, the password should be hashed using a new random salt. Never reuse a salt. Why not? The salt is still unique to that user. What's the benefit to changing it?
- MichaelGG 14y agoThere's zero cost to changing it, and makes any previous work on computing hashes for that user useless.
- ars 14y agoI use the hashed password as input in order to create short lived hashes for other purposes. By changing the salt, you change the hashed password. So when the user changes their password it actually changes, and those other hashes change too. If you kept the salt the same then if the user changes the password back to what it was nothing actually changes.
- pbreit 14y agoSo would this be a decent way to store the resulting hash? [method]$[salt]$[hash] sha256$fi93heyf789s2hfk$j2398fdperoc983m4n58djs20 Also, I like how Django's authentication can cycle through a list of schemes making it easy to switch or accommodate legacy accounts.
- pjscott 14y agoThe best way to store a password hash -- and I apologize for belaboring this point, but it bears repeating -- is to take whatever string you get from bcrypt or scrypt, and store it somewhere reliable with quick lookups, like a database with regular backups and replication. There are libraries that handle all the details of hashing a password, adding salt, making the computation too slow to easily brute force, and putting all this behind a trivial-to-use API. Use them!
- pbreit 14y agoWell, the article mostly discusses non-bcrypt solutions so the question seems reasonable. I suspect bcrypt is in limited use because it's not built-in to any of the development environments.
- Stefan_H 14y agoThis article left out one important item, in my opinion. You should also have a way of changing what algorithm is used in doing the hashing on the fly. Perhaps a "HashVersion" column so that you can easily migrate users from an older (possibly broken) algorithm, to a newer one as soon as they log in again.
- pjscott 14y agoThat's easily handled by, say, a column in a database table. Or just use something that's very unlikely to need replacing, like bcrypt. As far as I know, the only people who really needed to switch password hashing schemes in a hurry were people who chose laughably broken ones to begin with. (Hint for people who don't know this yet: salting and SHA256ing is an example of such a laughably broken password hashing scheme, so you can save yourself the hassle of migrating away from it by not using it in the first place.)
- Stefan_H 14y agoDoing a column in the database is exactly what I suggested... Also, saying that anything is "very unlikely to need replacing" is extremely short sighted of you. And the point is not to necessarily do it "in a hurry" but rather to identify a mechanism for doing it at all, in a way that doesn't require resetting every user's password.
- ZoFreX 14y agoThe nice thing about bcrypt is that it stores the work factor and salt and hash all in one value, so you can very easily migrate to harder work factors. I have seen similar schemes used to store hashes from a variety of algorithms, you can simply have something like bcrypt$workfactor$salt$hash stored in the password field.
- Stefan_H 14y agoYes, storing the algorithm used in the same column would be fine as well. But if you drop the "bcrypt$" from your example, you would still run into issues if bcrypt itself is broken.
- ars 14y agoAnother thing to remember is if the user changes the password be sure to invalidate any logged in sessions. You don't want the logged in sessions to act as a backdoor into the system. If you store the session key then delete them. If you calculate a session key on the fly (as a hash) then mix the hashed password into your session key, so if the password changes (even if it changes back to what it was) the session key will be different.
- cowkingdeluxe 14y agoDoes anyone else use either of these methods?: 1. Use memcached or asp.net cache to detect if a high number of login attempts are happening, if so, implement a 1 to 2 second sleep on each login attempt for 15 minutes (with some additional per IP slowing). 2. Put a sleep for 500ms on all login attempts. I've been doing both for a while, with the thought that they are effective methods in conjunction with proper hashing.
- ZoFreX 14y agoThey aren't bad ideas, but unless your hashing method is intrinsically slow your delays will be bypassed in the event of a database leak.
- courtewing 14y agoThis sounds like a reasonable addition to your security arsenal, and it should help to ensure that your own app is not being abused to brute-force your users' passwords. However, it doesn't provide you any protection if your database is compromised, and that is really when your hash security is most important. You do mention this in addition to "proper hashing", so I'm sure you recognize this as well, but I think it is important to emphasize secure hashing practices before any talk of securing your app's login endpoint itself.
- mechanical_fish 14y agoThis protects against people who are attacking your app through its public login interface, but when a file containing your hashed passwords gets stolen the thieves will attack the passwords directly. http://codahale.com/how-to-safely-store-a-password/ http://codahale.com/how-to-safely-store-a-password/
- junto 14y agoWhen I posted this, my intention wasn't to compare this against bcrypt as an appropriate method of storing passwords. I was more struck by the fact that the author had rather eloquently described both good and bad ways to salt and hash a password within the confines of the standard .NET framework. Notably, the bog standard, out-of-the-box Membership provider in ASP.NET uses the same algorithm as far as I am aware (while that doesn't make it the best, for many it may suffice their needs). Although there is an open source BCrypt port to .NET from Java, it hasn't been verified in terms of its implementation as a third party library, and to do so costs bucks. Therefore, the recommendation for .NET for increasing the compute factor is to use PBKDF2 instead of bcrypt since it is baked into the framework. It doesn't mean that is better, but if you are doing government work, then they will prefer you use a verified implementation, thus PBKDF2.
- blackhole 14y agoDo you realize that the phpass framework you recommend uses $2a$, even though you say not to use that?
- junto 14y agoJust to be clear, I didn't write the article, and I didn't recommend anything. If anything, it was posted as a discussion piece.
- blackhole 14y agoWell then the article is saying don't use $2a$ and then saying "oh but use this framework which also uses $2a$", which is terribly inconsistent. It should discuss when and where $2a$ could potentially be a problem.
- junto 14y agoFull steam ahead on the critique. I wanted to point out that you had no need to start bashing me about it!
- 14y ago
- zxtang 14y ago"Never send the user a new password over email." This is what HN does right now. I know, because I forgot my password just yesterday.
- uulbiy 14y agoSalted password hashing plain and simple: Rule 1: Use a Modern Hash Algorithm (bcrypt, PBKDF2, scrypt) Rule 2a: Use a Long Cryptographically Random Per-User Salt Rule 2b: Have an additional 'system' salt that is a fixed value for the entire system and it's not stored in the database (better hide it in the source code) Rule 3: Iterate the hash Source: https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet ------ For ASP.NET the only proven option for rule 1 is PBKDF2 which is builtin (http://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes.aspx http://msdn.microsoft.com/en-us/library/system.security.cryp...)
- pjscott 14y agoThat cheat sheet seems to have been written on the assumption that you're rolling your own, which is not a great idea. If you use Bcrypt, it handles salting for you, and you don't have to iterate it, since it has a tuneable work factor that you can tweak to make it faster or slower as your needs and Moore's Law dictate.
- whileonebegin 14y agoRegarding Rule 2b, why did the author keep repeating "store the salt in the database along with the key". Why would you want to do this? A cracker would have access to your hash and your salt. Wouldn't it be trivial for him to append the salt and then do a regular old lookup table on common passwords? At least if the salt is within the source code, it's hidden from plain view. Or did I miss something?
- ratzinho87 14y agoThe salt does not need to be hidden. First of all, it needs to be unique per hashed information (password), so you cannot store it in code. Second, its purpose is to force any attacker trying to use a lookup tables to calculate one lookup table per password. http://en.wikipedia.org/wiki/Salt_%28cryptography%29 http://en.wikipedia.org/wiki/Salt_%28cryptography%29 Edit: also, instead of using a "system salt", why not use an HMAC to replace hash function?
- tptacek 14y agoAny article that says you need to carefully generate cryptographically random long salts shouldn't be trusted. No modern password cracking technique relies on predicting salts. Think about this for just a second and you'll see why.
- zokier 14y agoDoing it right for most cases mean not doing it all. You shouldn't be thinking about hash functions and salts. Instead you should use a cryptosystem designed and implemented by experts. DO NOT DO CRYPTO YOURSELF
- Ralith 14y agoThe problem is you still have to go out and successfully identify which cryptossytem was designed and implemented by experts and is appropriate for your use-case.
- pjscott 14y agoVery right. A lot of people do something naive like just hashing passwords with a standard cryptographic hash function, figuring that they're designed and implemented by experts, and they end up with a careful implementation of the wrong algorithm. Of course, in the case of password hashing, the answer is pretty easy. (Spoilers: scrypt if there's an easy library for your language of choice, bcrypt otherwise, and PBKDF2 if you need to justify your decision to someone who habitually wears a tie.)
- FuzzyDunlop 14y agoValid point, but what if you don't know who the experts are? You're preaching to the choir on HN when it comes to PBKDF and Bcrypt and Scrypt and all that; but outside of our circle, people will consider anything documented on the net to be potentially expert advice. Check this for a bunch of custom password hashing functions: http://php.net/manual/en/function.sha1.php http://php.net/manual/en/function.sha1.php
- dchest 14y agoNot this again! How can you give advice to use bcrypt/etc. and also suggest the following function? function HashPassword($password) { $salt = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM)); //get 256 random bits in hex $hash = hash("sha256", $salt . $password); //prepend the salt, then hash //store the salt and hash in the same string, so only 1 DB column is needed $final = $salt . $hash; return $final; } It's becoming ridiculous.
- mikek 14y agoWhat is wrong with the above function? It's not obvious to me.
- pjscott 14y agoSHA256 is very fast, which makes it relatively easy to brute-force if someone gets their hands on your hashed password file.
- pbreit 14y agoEven with long, unique salts?
- pjscott 14y agoYes. A GPU can calculate more than two billion SHA-256 hashes per second, and adding 32 random bytes to each hashing is not going to slow it down enough to matter. Salting protects against rainbow tables; it's not an effective protection against someone just trying to brute-force guess passwords.
- xvolter 14y agoThis is true. The bruteforce attempts will include the salts in, simply adding to the length of the textdata being hashed. After they get the matching string to the hash, it's an easy enough process to then figure out which part is the password and which is the salt. Attempts to get around this are done by things such as: md5(sha1(md5(md5(password) + sha1(password)) + md5(password))) Which is not an appropriate method to circumvent. SHA512 is obviously a bit most costly, and therefore harder to bruteforce, but if you truly care about security you would be best to use PBKDF2 at minimum (built into Django's standard).
- gshaw 14y ago"In step 4, never tell the user if it was the username or password they got wrong. Always display a generic message like "Invalid username or password." This prevents attackers from enumerating valid usernames without knowing their passwords." What prevents the same enumerating attack against the sign up form. Are you going to give them a generic message that the username is invalid when it in fact has been taken? Also the article implies that you need to use salt but than recommends using bcrypt which already includes salt. Good read on how passwords are attacked.
- darkarmani 14y ago> What prevents the same enumerating attack against the sign up form. Good point. Nothing prevents this, but it is easier to detect this kind of abuse on the sign up form and alert on it than all of the noise on the sign-in side.
- kamkha 14y ago"What prevents the same enumerating attack against the sign up form. Are you going to give them a generic message that the username is invalid when it in fact has been taken?" There are, more frequently, CAPTCHAs on registration forms.
- harshreality 14y agoImmediate username availability checks (via ajax) are fairly common, and they bypass captchas.
- pygorex 14y agoThis is a point where you'll have to judge the security considerations against usability considerations. As a user I might end up hammering a web service with correct passwords but incorrect usernames. As a user knowing that I'm using the wrong username would be useful and keep me from getting frustrated. A better solution would be to rate limit incorrect username guesses. It's highly unlikely that a user is going to try more than a dozen usernames/emails - so that's strong signal that someone is trying to leak username information from your database.
- 14y ago
- ghshephard 14y agoObligatory: http://codahale.com/how-to-safely-store-a-password/ http://codahale.com/how-to-safely-store-a-password/
- cowkingdeluxe 14y agoI also recommend: http://www.codinghorror.com/blog/2012/04/speed-hashing.html http://www.codinghorror.com/blog/2012/04/speed-hashing.html
- skrebbel 14y agoA question for the people here who do know: Coda Hale says "Use bcrypt. bcrypt. bcryptbcryptbcryptbcrypt", and appears to get a lot of support from those in the know. Right, got the note, makes sense. Now, does PBKDF2 count, too? I do a lot of .NET and it's built-in and "verified" (whatever that means) there.
- pjscott 14y agoPBKDF2 is fine. It's not as good as bcrypt, and since bcrypt libraries are trivial to use and pretty ubiquitous most people should probably just use one, but PBKDF2 is okay.
- jemfinch 14y ago"Prepend the salt to the password and hash it with a standard cryptographic hash function such as SHA256." No! At the very least, you ought to be using an HMAC.
- pjscott 14y agoThat's potentially dangerous advice, because as it may lead someone to use an HMAC and think themselves safe, instead of using something like Bcrypt that's slow enough to foil brute force attacks.
- wglb 14y agoThe phrase A brute-force attack tries every possible combination of characters up to a given length. These attacks are very computationally expensive suggests that the author has not spent any quality time with JTR. Computationally expensive would not be equivalent to cracking billions of passwords per second which is what you can get with sha* and salts.