Hacker News new | ask | show | jobs
by boomer918 1618 days ago
If the salt is stored with the file, how is this safe against a brute force attack against the low entropy password?
5 comments

The purpose of a "salt" is just to randomize the hash; an attacker can precalculate a dictionary for a hash function H, but they can't plausibly precalculate 2^128 dictionaries for the family of hash functions H_nonce.

People get hung up on this, because the nonce looks like it could serve as a key; if you keep the key hidden, an attacker can't brute-force your hash at all. The obvious response to that is: if you can do keep your nonce secret like that, just get rid of the passwords, and key your system with actual keys. Or, store your passwords in the super-secure place you store the nonces.

These discussions quickly rabbithole into analyses of the varying levels of security between filesystems, HSMs, program memory, networked filesystems, the kernel, VM boundaries, and the difficulty for an attacker of assembling all these components at once. It's all pretty silly. But the answer to your question is simple: a salt (or nonce) isn't a key; that's not the purpose it serves in the design. If you really want to key your password hash, you don't need to muck with the salt to do that.

In this case, the security is in the memory and/or time hardness of the KDF: a motivated attacker could use the salt with their dictionary, but would have to be willing to wait (on average) a decent, perhaps deterring, amount of time (or similar for memory).

Edit: The defaults for the argon2 crate are here[1]. They seem to prioritize time cost over memory cost. Some random searching online suggests that a time cost of 3 corresponds to roughly ~2 seconds on modern hardware, so running a 100k dictionary with a time cost of 3 would require ~27.7 hours for the amortized find (50k) or ~55.5 for the worst case find. So, this isn't a very good scheme for a motivated (or parallel) attacker and an exceptionally weak password.

I think the short answer is "don't be in a dictionary". Using a unique password is critically important.

Let's imagine that you increased the time by 10x. That's 277 hours for a password. That's not very long at all - 12 days. Even if you increased by 100x, 120 days is not crazy, and presumably attackers can go way faster than your assumption.

A KDF isn't going to be enough to save you if you're using a top 100k password and the attacker can bruteforce offline.

"Don't be in a dictionary" is trivially easy to solve with generated passphrases: just pick a bunch of random words and string them together. You can generate an arbitrary amount of "entropy" this way.

Of course, users won't do this for themselves, which is why tools that do passphrase encryption should generate passwords by default, and accept user-provided passwords only as a non-default option.

Passphrases still have value, even when they're long strings of words: they're easy to write down, easy to repeat aloud, and easier than a random string to "recognize" visually.

It should probably be regarded as "relatively secure" encryption. You can't have truly secure encryption if your passphrase is garbage.
My understanding is that the key derivation function chosen, argon2, is much more expensive to compute than something like the SHA family of hashes. This is a desirable property in a KDF precisely because it makes brute forcing much more difficult.

Further, argon2id incorporates strategies to make GPU parallelization less effective.

Obviously this won't protect you against something like a dictionary attack, there's nothing that can magically protect you if you choose a low-entropy password, just something that can make the process more difficult.

It does seem like talking about dictionary attacks in the article would be helpful. Without some reference as to how many argon2 hashes per minute is reasonably possible with the shown settings, we're flying a little blind.
I performed a rough estimate in my comment up the thread, using ~2s per Argon2id with a time cost of 3. TL;DR is that you probably wouldn't want to have an extremely common password with this scheme.
There's no KDF in the world that can protect you if your password is in a top-10,000 list or exposed elsewhere alongside your username.
Right, but that's sort of what I was getting at. The article doesn't talk much about the password other than it's "insecure". It's probably worth mentioning that a dictionary attack at some multiple of ~2/per-second/per-core is possible. So it's not just top-10,000 list, but maybe "top million" or more that's a bad idea.
Yes, I think that's what the GP was trying to say. The post doesn't qualify "insecure" meaning "not best practices" vs. "insecure" meaning "your password is an extremely common one."
> there's nothing that can magically protect you if you choose a low-entropy password

Ignoring the extra time for decryption there's no difference between a unique low-entropy password that takes 2 years to bruteforce and a high-entropy password that takes 2 years to bruteforce.

Yeah, if you a very common password like "hunter2" or "Password1", then even with a KDF that takes 100ms to generate the key, it's still very feasible to run through the 100k most common passwords and compromise it within a few hours.

If we're talking a more random but still short password (for example, just 8 random alphanumeric characters is log2(262+10)8 = ~48 bits), then the KDF becomes very attractive to help skyrocket the brute forcing cost to something more similar to trying to brute force the 256 bit key instead.

Good question!

I will add a conclusion with an explanation of the security of the system.

TL;DR: You can play with Argon2's parameters to use the resources that fit your requirements.