Storing Passwords in a Highly Parallelized World
hynek.me
hynek.me
The real, big risk is not using a password hash at all, and instead using "salted hashes".
> The real, big risk is not using a password hash at all, and instead using "salted hashes".
If so, your statement is incorrect. Without salting you are vulnerable to trivial rainbow table attacks [1]
Salting is one of the best and easiest measures if there is a possibility of lowish entropy passwords, but if not it doesn't help (edit: on further thought I think there is a fairly substantial entropy range where the only viable attack is a very large scale batch attack and salting would prevent that, although the cost/benefit on actually doing that makes it an unlikely attack anyway). Iteration and memory hard functions also effectively add just a few bits of entropy. But when users choose passwords, a huge portion will be low enough entropy that nothing you can do will help enough.
A second factor does help in that case, and can provide additional protection to high entropy passwords (as long as the second factor is distinct from the system that stores the high entropy password, which in theory it should be to be called a second factor).
technion: yeah, my suggestion won't work if there is any way for the user to set the password.
I don't see the server relief in Argon2's latest specification document though.
Booleans are used in a variety of (popular) Python libraries when checking whether a password is correct (e.g. Django's `check_password` returns False if the password is wrong).
def handle_login():
try:
ph.verify(hash, "s3kr3tp4ssw0rd")
log_login()
redirect_to_page()
except VerificationError:
log_bad_login()
redirect_to_login() def handle_login():
try:
ph.verify(hash, "s3kr3tp4ssw0rd")
except VerificationError:
log_bad_login()
redirect_to_login()
else:
log_login()
redirect_to_page()
It's not a big deal for this code, but in general this is good practice because1. It makes it very obvious to the next developer which line is the one that is expected to raise that exception
2. One of the other lines could unintentionally raise that exception and mistakenly trigger the except clause. (This is more of an issue with Python's built in exceptions than with something very specific like this `VerificationError` example.)
1. mainly: the C library has no concept of “wrong password”; only “verification failed with an error”. If you want to know why it failed, you need the error. As you can see in the example, a wrong password is "Decoding failed” which can also be your fault. It seems like they want to interpret their own failures as little possible. Therefore raising an exception with the error seemed the best way forward. 2. secondarily: in security context, I tend to prefer loud failures for dangerous problems so they don’t pass unnoticed by accident. ymmv
Just ask Bitcoin miners how hard it is to pick an input which results in a hash with a desired n-bit prefix.
But as a belt-and-suspenders you often see an attempt at fixed time comparisons of digests in any case.
Coincidentally, hashing before comparing can be used in scripting languages where the compare function will often be optimized out from under you, making constant time compare difficult or impossible to actually guarantee.
A machine with a measly 256GB of memory can do around half a million Argon2 hashes simultaneously (using the Python library defaults for memory complexity of 512k), and that doesn't include the memory built into high end GPUs, or what could be added to ASICs or FPGA boards.
A machine with a measly 256GB of memory can do around half a million Argon2 hashes simultaneously
Not necessarily. There is always the limitation of memory bandwidth. Although, I do not see saturating that bandwidth as a design goal of Argon2.
https://www.npmjs.com/package/bcrypt
https://github.com/ranisalt/node-argon2
edit: found it. I'm still reading
> Argon2i is more vulnerable to tradeoff attacks due to its data-independent addressing scheme. We applied the ranking algorithm to 3-pass Argon2i to calculate time and computational penalties. We found out that the memory reduction by the factor of 3 already gives the computational penalty of around 214. The 214 Blake2b cores would take more area than 1 GB of RAM (Section 2.1), thus prohibiting the adversary to further reduce the time-area product. We conclude that the time-area product cost for Argon2d can be reduced by 3 at best.
On the other hand, we still see password databases stored in plain MD5, sometimes even without salt. So in addition to provide better password hashes, making them mode widespread is important, too.
I suppose the answer is yes, but it's worth it. It's possible to fix this sort of hole (partially, at least) by capping the number of hashes processed concurrently. But most simple implementations will just assume "we're not likely to have more than X users every signing in concurrently, so we can set the work factors based on that plus some headrooom".
You're right however that this is going to be painful, because most web frameworks don't have any of this throttling architecture (like persistent in-memory hashtables) in place, and as soon as they switch to Argon2/Scrypt, it's going to be an easy DoS vector... particularly for services running on weak VPSs.
It's another reason why solid, secure password authentication protocols, that do client-side hashing, will eventually happen. Even in fancy zero-knowledge asymmetric protocols however, online rate-limiting is essential.
Per-IP per-account as well doesn't work if the attacker has a large list of usernames. Even brute-force "dictionary" attacks can dodge simple limiters by submitting one password with 2 million diff usernames, then a second password with 2 million usernames, etc..
I'm not saying these are bad (though if someone can trivially stop your real users from signing in by hitting the limit on their accounts, that's just a DoS in another shape). But we're agreed already... these are non-simple problems, really.
You mean, like scrypt, seven years ago?
ph.hash("s3kr3tp4ssw0rd")
ph.verify(hash, "s3kr3tp4ssw0rd")
Is pretty damn easy.It's the problem area that is complicated and rapidly changing. And use of APIs and mechanisms that just a few years back were Best Practices is now discouraged. That's not something that can be treated by a cute Python API.
And OAuth is no exception: If you implemented OAuth five years ago, it was OAuth1, which was obsolete and ripe for replacement… four years ago.
And the OAuth point is misdirected: we're talking about hash choices here, which OAuth is not. An OAuth1 client, while perhaps obsolete for other reasons, would certainly be protected against the need for a change in password hash, because all that stuff happens upstream and it never sees the password. That is by design, therefore remote authentication against highly-trusted password verifiers[1] is a good idea, which is what the great-grantparent post was saying.
[1] Which, sure, has its own list of worries unrelated to hash behavior.
Ergo, if you're worried about changes to password hash vogues, OAuth is a good idea. Thus the point way upthread, which both you and the other poster seem to have missed.
If bcrypt gets broken tomorrow, your passwords are safe until you have a data breach. If OAuth gets broken tomorrow, you're immediately at risk.
This is the kind of thing that made me feel like the Password Hashing Contest might be a net negative for systems security. In reality, despite what this blog post says, you could select the worst of the password hashes --- PBKDF2 --- in relatively weak parameters, and still be fine.
If you give nerds 3-4 things that do the same thing, we will find 10^(3-4) different ways to have arguments about them.
Assuming that it's well-designed, Argon2's structure shouldn't rely on BLAKE.
Keccak is known to be very fast in hardware, which opens up the path to highly optimized cheap circuits.
(although with strange follow-up: "but the same can be said about modern CPUs and GPUs, which closes the gap")
As I wrote when discussing candidates, "I like the simplicity of Gambit, but it would look a bit silly if we selected an algorithm as a winner of competition with a notice that it better be used in the future, when Intel adds a SHA-3 instruction."
Bcrypt, scrypt, and argon2 are for server storage of login information. The server does not store the actual password, it just stores a hash.