Salted Password Hashing – Doing It Right
crackstation.net
crackstation.net
For PHP Devs, these are the two best options available:
"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. "
Completely, utterly wrong. It 'looks' secure. We're not telling the user if it's a login or password.
But if you try to create an account with the username, it tells you "CANNOT CREATE, USER ALREADY EXISTS".
So, it's an anti-pattern that serves to confuse the user but fails to stop a scripted attack.
Then don't do it, give a generic error message and send an email to the accounts email address saying "Did you try to sign-up, you already have an account, here is the reset link in case you forgot the password"
I know of no situations that seed "unobtainable usernames" in a DB. Although now, I would expect that.
For user enumeration on registration you have two choices:
1. Provide a unique registration link to an email for signup. The prospective user goes to the registration page, submits and email, checks their email, then follows the link to a registration page. On this page it's easy to see if someone is enumerating usernames, and every request contains the token from their unique signup link so the token can be invalidated quickly.
2. Provide a slow rate limited lookup to verify an existing username, every 2-3 seconds, which shouldn't negatively affect users as they signup because they have to fill out a registration form in the mean time. This will however greatly slow down a bot.
You can have a website that tells you "Bad password", and the user knows what it is. They fix it, on they go.
If there's a bad actor, it says "bad password". Same warning. Now, you slow down the response so that after 5-10 times, the website gets dog-slow. It's still useful if you're a legitimate user.
And if someone is script-attacking you, they're going to get an enumeration of your usernames by using a different script. Or they'll dump the username:passwd database if they get in. In that case, obfuscating usernames is moot anyways.
It's security through obscurity, and it's not that obscure. And it's bad UI.
The slowed down responses (exponential back-off) is a the preferred method of rate limiting over account lockout.
"Or they'll dump the username:passwd database if they get in. In that case, obfuscating usernames is moot anyways."
Well if they can dump your DB it's game over for your whole app and possibly infrastructure anyways.
Why not? Rather than parroting conventional wisdom, how about making a cost/benefit case?
We're pitting a significant UX benefit against a tiny marginal security benefit.
Unless you're demanding a higher level security across the entire service, in which case you don't want user enumeration anywhere, then the correct answer to implement throttling on anything that would make the usernames discoverable. That's more flexible solution that does not conflict with good UX.
I agree, implement throttling anywhere you have authentication or registration, that's a good idea to have in place in any case. I'm not arguing that.
It seems there is always the problem of balancing security, performance, and usability. You just have to decide which is more important when examining features. I this case I would advocate security. The decision you make with your applications is up to you, but it's important to understand you are making a tradeoff.
Old (forum, ...) accounts, >5 years of inactivity. What e-mail did I use for this again? I end up just typing every e-mail address I can remember in the reset password field because it's faster than the combinatorially exhaustive search with the three / four probable passwords from way back when.
EDIT: My main objection is that taking computer literate people as the litmus users is a UX anti-pattern. We talk a lot, but we easily forget just how much hand-holding, clarity and patience a large (and silent) chunk of our users need.
User enumeration attacks are real and bad. A better solution to that would be to not leak this information when trying to create an account, instead. Since most sign-up processes seem to require email confirmation anyway, nothing is really lost there.
Of course, if you don't require email confirmation, then go right ahead – you'll have to use other approaches to avoid enumeration (like rate limits), but then you should be doing that anyway.
But if you want PBKDF2, defuse's github has more up-to-date code that, IIRC, he hasn't taken the time to copy/paste into the page linked.
There's also another implementation in PHP-CryptLib by Anthony Ferrara, the gentlemen that brought the current password API to PHP. https://github.com/ircmaxell/PHP-CryptLib
$sHash = password_hash('ThePassword', PASSWORD_BCRYPT, ['cost' => 12]);
Checking is as easily as: if (password_verify('ThePassword', $sHash)) {…}
Or do you guys have other thoughts?That's fine, but it's not the full story, and as such, does a disservice for those wanting to know about securely storing passwords on disk. PBKDF2 and bcrypt can be a performance killer for authentication applications, if the host is a busy host. Instead, changing the number of rotations on a fast hashing algorithm gives you finer granularity on brute force speed versus application and host reliability.
This is probably the best commentary I've seen on the topic of how many rounds you should incorporate into your KDF (Scrypt/Bcrypt/PBKDF2 all let you dial as many as you wish), so I won't try and improve on it: http://security.stackexchange.com/a/3993
If you have typical users, with typical user passwords, then take a few minutes to read through: http://security.stackexchange.com/a/3993 (it discusses the importance of taking into account entropy) to determine what work factor is appropriate for your situation, then just deploy one of scrypt/bcrypt/PBKDF2 with that work factor.
The argument here seems to be that if people simply don't use passwords, but instead secure AES keys, password hashes won't matter. Ok, but we're talking about passwords, and so was he, upthread.
Given that the strength of security is measured in how long it takes to break it, and hash time is directly correlated with brute force time, for the same password, faster is weaker.
Having high-entropy passwords certainly increases security, but it's a well-known fact that users don't use high-entropy passwords, so that's pointless to talk about.
This is a solved problem: use 10,000+ rounds of PBKDF2, 4-5+ rounds of Bcrypt, or a sufficient work factor for Scrypt.
None of that changes the fact that the _more secure_ thing to do is implement bcrypt/scrpt/PBKDF2. I bet there are plenty of people at Sony, Target, Adobe, Home Depot, Staples, etc. that wished they had taken the more secure route and spent that extra money.
For applications requiring basic auth, you could cache the username/password combination after the first login.
For bcrypt to be a killer on modern systems, you are botching something in your authentication system.
Hardware slices through iterations of fast hash functions --- functions that are designed to be quickly executed on hardware --- like a red-hot knife through warm butter. Developers who think they're getting "fine-grained control" over brute force speed by tinkering with SHA2 are deluding themselves. PBKDF2 is not simply the repeated application of SHA2.
Don't use fast hashes for passwords. Don't design constructions that use fast hashes for passwords. That work has been done for you already: use scrypt, instead of an underspecified, half-functional, and insecure homebrew approximation of scrypt.
Let me know when MySQL supports bcrypt/scrypt.
I know Oracle has pretty sophisticated stored procedures, but even so, most of the application logic of systems I've seen had program code written in an "external" language.
Yes salting prevents as effective brute forcing. Yet modern computing 8 char passwords + salt can be exhausted within 24-48 hours (on a <$20k GPU cluster) if you using SHA-1/2 + salt.
bcrypt/scrypt/PBKDF2 are much harder to brute force (by design) and circumvent this weakness.
Salts do zero to mitigate brute-force attacks.
Salts defend against one idiosyncratic lookup-table attack that was briefly popular in the early 2000s because of a widely-deployed password hash function that, unlike every password hash since the mid-1970s, didn't randomize. This is the attack to which "rainbow tables" are applied.
If you're considering rainbow tables as part of your threat model, you're already so boned that you should give up and just use a real password hash. If you're not considering rainbow tables, you don't have to consider salts.
The only good thing about "strong salts" is that they function as a kind of shibboleth so you can tell if people understand what problems they're contending with when storing passwords.
Back then systems used no password at all or stored it in plain text.
In the mid-1970s the NSA just finished DES.
Password shadowing first appeared in UNIX systems with the development of System V Release 3.2 in 1988 - see: http://en.wikipedia.org/wiki/Passwd#History
> "briefly popular in the early 2000s"
Til the dot-com-bubble timeframe many websites stored plain text passwords in databases, later they shifted to SHA1/MD5 hashed passwords without salt.
Only with the Sony hacks a few years ago, many added some kind of salting.
To head off an unproductive discussion, I'll just repeat:
* salts do zero to mitigate brute-force attacks
* even trivial salt schemes break table-based attacks
* the overwhelming majority of passwords are cracked through brute-force, which is attack vector that real-world password hashes need to be evaluated on.
If anyone wants to claim that salts do anything to stop brute-force attacks, take a sample of your passwords and run it through FL Cracker:
Key stretching with SHA-1 or SHA-2 is hard to do right, and if you do it right, you've reached a solution that is basically equivalent to PBKDF2 or bcrypt. If you're interested in how PBKDF2 or bcrypt are implemented, that's one thing, but if you're rolling your own password stretching on top of SHA-1 or SHA-2, you're probably not doing it correctly and performing [security theater](http://en.wikipedia.org/wiki/Security_theater).
> PBKDF2 and bcrypt can be a performance killer for authentication applications, if the host is a busy host. Instead, changing the number of rotations on a fast hashing algorithm gives you finer granularity on brute force speed versus application and host reliability.
This is already provided by PBKDF2 and bcrypt. Both can be configured to run more or fewer rounds.
Cudos to the author.
ITERATIONS = 600
...
crypto.pbkdf2 pwd, salt, ITERATIONS, LEN, (err, hash) ->
That's way too small for the number of iterations. Something like 100K would be a better choice.Alternatively here's a version that uses bcrypt:
bcrypt = require 'bcrypt'
rounds = Number(process.env.BCRYPT_ROUNDS || 12)
module.exports =
hash: (password, cb) ->
bcrypt.hash password, rounds, cb
compare: (password, hashedPassword, cb) ->
bcrypt.compare password, hashedPassword, cbTest program:
crypto = require 'crypto'
password = 'testing'
len = 128
salt = crypto.randomBytes(len)
iters = Number(process.argv[2] || 600)
console.log 'Testing iters=%s', iters
for i in [1..10]
start = Date.now()
crypto.pbkdf2Sync password, salt, iters, len
elapsed = Date.now() - start
console.log ' Test #%s - %s ms', i, elapsed
Output: $ coffee pbkdf2-test.coffee 100000
Testing iters=100000
Test #1 - 497 ms
Test #2 - 510 ms
Test #3 - 496 ms
Test #4 - 525 ms
Test #5 - 510 ms
Test #6 - 493 ms
Test #7 - 521 ms
Test #8 - 518 ms
Test #9 - 510 ms
Test #10 - 498 ms
$ coffee pbkdf2-test.coffee 10000
Testing iters=10000
Test #1 - 54 ms
Test #2 - 50 ms
Test #3 - 50 ms
Test #4 - 55 ms
Test #5 - 51 ms
Test #6 - 52 ms
Test #7 - 50 ms
Test #8 - 49 ms
Test #9 - 51 ms
Test #10 - 50 ms
$ coffee pbkdf2-test.coffee 600
Testing iters=600
Test #1 - 3 ms
Test #2 - 3 ms
Test #3 - 3 ms
Test #4 - 3 ms
Test #5 - 3 ms
Test #6 - 4 ms
Test #7 - 3 ms
Test #8 - 4 ms
Test #9 - 3 ms
Test #10 - 3 msFor me 1 second seems pretty aggressive, that's CPU time/latency per login.
The solution to this is zero-knowledge password proofs[1]. Essentially, you should never be passing your password to another party. Instead, you should be providing the authenticating party with a prover (analogous to a public key in asymmetric-key encryption, but based off your password) when you sign up. Then when you want to authenticate, you provide the server with a proof based off your password which exposes no information about your password but can be used to verify that you have your password using the prover (analogous to a signature in asymmetric-key encryption, but again based off your password). There are a number of ways to generate these provers and proofs using existing asymmetric-key algorithms such as RSA, ElGamal, or ECDSA.
Once this becomes pervasive, browsers and other network applications can provide obnoxious security warnings similar to those currently provided for untrusted certificates. This will create an up-front cost for people still using password hashing and shame them into moving to ZKPP, while providing a user-friendly way for users to tell if their password is being used insecurely. Only once people are no longer giving their passwords to anyone with a login page can we be sure that poor security on other sites won't compromise our own servers.
EDIT: Additionally, ZKPP mitigates some of the damage caused by man-in-the-middle attacks. Currently, if an attacker can gain access to communications between a user and server, they can access the password in plaintext when it is passing over the wire, even if it is adequately hashed on the server. This has both permanence (the password continues to be useful after the vulnerability is discovered--until the user changes their password) and wide application (the password can be used on multiple sites).
With ZKPP the attacker only has access to the proof of the password. This might allow the attacker to authenticate with the server during the attack. However, a proper ZKPP implementation uses unique nonces from the server to create the proofs and rejects non-unique proofs, so the proof cannot be used after the vulnerability has been fixed (the server will check that it is unique) and it cannot be used on other sites (the nonce is provided by the server).
[1] http://en.wikipedia.org/wiki/Zero-knowledge_password_proof
This post was written years ago.
> Whether or not a service stores passwords correctly cannot be verified by the user, and as such users must use different passwords for every site.
Actually, in PHP applications, if passwords are stored plaintext or via unsalted MD5/SHA1 and are compared with the non-strict == operator, we can test it remotely.
Granted, this is technically exploiting a security vulnerability, but it's pretty conclusive if it works. :)
As for ZKPPs, I'm looking forward to SQRL for anonymous, Ed25519 challenge-response based authentication that takes place without requiring technical knowledge, personally.
<?php
$a = md5('240610708');
$b = md5('QNKCDZO');
echo "$a\n";
echo "$b\n";
echo "\n";
var_dump($a == $b);
Run that code.Then change the == to an === and observe the difference.
The problem is not PHP, the problem is ignorant developers who do not know [how] to use password_hash() and password_verify().
[Also, "Matasano". I feel like I'm pressing the summonthensa.com button but w/e.]
What's especially ironic about your comment is that password-authenticated key exchanges still require servers to store authenticators which can be attacked offline. PAKEs actually make the problem we're talking about harder, not easier, because the authenticator format has to be compatible with the protocol.
If you're asking that question you don't understand the purpose of SRP or ZKPP. If you're giving a password you use elsewhere to a website, you're trusting them with your private data, and you shouldn't. As developers it's our jobs to prevent users from making this mistake if we can.
When using SRP or another ZKPP scheme, trust doesn't enter the equation. You aren't giving the server your password, you're giving them a piece of data which is verifiably difficult to reverse to obtain your password, so you don't need to trust them to store it properly.
> What's especially ironic about your comment is that password-authenticated key exchanges still require servers to store authenticators which can be attacked offline.
This isn't ironic: this is exactly what I would expect from kerkelsager's comment. Again you're just showing your ignorance. Yes, you can attack an authenticator. The point of ZKPP is that it allows the client to verify that such an attack is computationally difficult. If you've given the server your password in plain text (or reversibly encrypted form), you can no longer verify that obtaining the password from the authenticator is computationally difficult. For all you know, the server stores your password in plain text. This isn't a theoretical problem: there are numerous examples of this happening.
> PAKEs actually make the problem we're talking about harder, not easier, because the authenticator format has to be compatible with the protocol.
If you're objecting to implementing things correctly because it's hard, software might not be the right career for you.
Think from the perspective of a non-technical user. You're lazy, so you want to use a short password that's easy to remember, and you want to use it on every website.
Now think from the perspective of a software engineer. Your non-technical users will use short passwords that are easy to remember and therefore easy to break. And they will use that password on every website. So no matter how you store your user's passwords, some idiot server administrator somewhere will store those same passwords in plain text, and they'll get hacked, and the hackers will have access to your user's passwords.
So the problem isn't "how do I store passwords securely", it's "how do I prevent my users from giving their passwords to an idiot server administrator who won't store them securely".
And as a technical user, I also have this problem, which is "how do I know if my server administrator is an idiot and is storing my passwords in plain text?" The solution to this is to use different high-entropy passwords on different sites, but this is tedious.
If ZKPP (zero-knowledge password proof) is widely adopted and supported in browsers, it solves both the server administrator's problem and the technical user's problem:
1. It stops idiot server administrators from storing passwords in plain text. The "zero knowledge" part of ZKPP means that users never give server administrators their password or any knowledge about their password, so administrators can't store the password stupidly.
2. As a technical user, I can now use the same password on multiple sites, provided a) my password is significantly entropic and b) all those sites use a ZKPP scheme. Since I never give the password to any site, I don't have to worry that one of them will store my password stupidly. My browser does the job of providing different authenticators to different sites, so breaking one authenticator doesn't break all of them.
3. A non-technical user with a low-entropy password on multiple still runs the risk that their password will be broken one one site, breaking it for all sites. However, the non-technical user is still better off because a) their password is at least properly hashed on all sites, rather than being stored in plain text, which will make an attack more costly, and b) caching password hashes amortizes the performance hit of password hashing, so we can afford to use more secure hashes, which again makes an attack more costly.
NOTE: I don't know the details of SRP well enough to know whether all these benefits apply. I do know that SRP claims to provide a ZKPP, so I'll tentatively lump it in with ZKPPs, but I haven't actually verified the algorithm personally so I can't speak to its security.
Go through the exercise of cracking an SRP authenticator to see the problem with this logic.
Weirdly, despite saying upthread that I must not know how SRP works to say what I'd said, you now say that you're not familiar with SRP. If it helps, substitute your favorite password protocol instead of SRP. I believe the problem will be the same.
If not: happy to learn something new.
There are mathematical proofs of SRP's security. Yes, there are some issues with some implementations of SRP, but those are problems with implementations, not with SRP. Frankly, this is just one of those arguments at this point where it's clear you don't actually understand the fundamentals of security and you're too committed to your argument and too proud to admit you don't know. Since you can't actually make an argument, you're just presenting me with difficult/impossible tasks and claiming that if I did them it would prove your point, knowing full well that I won't do them. If you actually were knowledgeable on this topic, you could explain it: I have explained everything I said so far.
> Weirdly, despite saying upthread that I must not know how SRP works to say what I'd said, you now say that you're not familiar with SRP.
I don't know all the exact implementation details of SRP, but I do understand the general architecture. The problem is that the devil in security is often in the details. I know enough to know the limitations of my knowledge. The important thing for the sake of what I'm saying is that I know what problem SRP is trying to solve.
> If not: happy to learn something new.
The only thing I'm going to try to teach you at this point is this: if you're going to even bother forming an opinion on something technical, you had better be damn sure you're right. Because if you're wrong, it's human nature to react negatively when someone tells you you're wrong, so you'll try to argue about something you're wrong about, and the result is: you'll never learn, and you'll probably impress some people who know even less than you, but you'll embarrass yourself in front of anyone who actually knows what they're talking about, and you'll limit your progress as an expert in your field. You'll never get smarter until you stop trying to prove how smart you already are.
Completely wrong. People reuse passwords all the time.