A reviewer asks why a stored password hash needs a per-user salt when the database is already encrypted at rest and the hash algorithm is strong. Which answer is correct?
Show the full answer Hide the answer
What is being tested
Whether you can name two separate defences that are often confused into one, and whether you know which threat each one answers. The question is also a test of threat modelling: encryption at rest and password hashing defend against different adversaries.
The mechanism
Assume the attacker has the table. That is the scenario password hashing exists for, and it is the one encryption at rest does not cover - at-rest encryption protects a stolen disk or a decommissioned drive, and a dumped table arrives through the application's own authenticated connection, decrypted.
Given the table, two costs matter:
- The salt removes amortisation across users. With no salt, one precomputed table of candidate passwords can be matched against every row at once, and identical passwords are visibly identical rows. A unique per-user salt forces the attacker to attack each row separately. It does not make any single guess harder - it removes the discount for attacking many accounts.
- The key-derivation function sets the price of one guess. A general-purpose hash is designed to be fast: a single modern GPU computes on the order of billions of fast-hash candidates per second. A password-hashing function is designed to be slow and, for the memory-hard family, expensive in memory as well. Argon2id, specified in RFC 9106 (2021), is parameterised on memory, iterations and parallelism precisely so that the defender picks the cost; bcrypt exposes the same idea as a single cost factor. Tuned so that one verification takes roughly 100 ms on your hardware, a commodity GPU manages thousands of guesses per second rather than billions - a reduction of six orders of magnitude, and that factor is the entire protection.
Both are needed: a salted fast hash is cheap to attack per user, and a slow unsalted hash lets one table cover everyone.
Why the other options fail
- "Encryption at rest covers it." The most common and most consequential error. Nearly every published credential breach involved a database that was encrypted at rest; the data was obtained through the application or a backup, where it is plaintext.
- "A longer salt makes the hash harder to invert." Hash inversion is not the attack. Attackers guess passwords and hash the guesses, so the defence is the cost of a guess. A salt of 16 bytes is sufficient, and 64 would not help.
- "Only needed for weak algorithms." Backwards. A strong, fast hash such as SHA-256 is the worst choice here precisely because it is strong and fast, and the salt is what stops one table serving the whole dataset.
When this is the wrong concern entirely
When you do not store passwords. If authentication is federated to an identity provider, or uses passkeys, there is no verifier in your database to protect and the work shifts to token handling and account recovery. The strongest version of this control is not storing the secret, and for a new system that is the first option to evaluate. Where you must store them, the rules are short: a unique random salt per user, a memory-hard function with parameters tuned to your hardware and re-tuned every couple of years, the cost factor stored with the hash so it can be raised, and a re-hash on next successful login when you raise it.