practice · 02 / 02salt + slow hash

Password Storage

Why a leaked database still shouldn't give up your password.

What it does

The single rule of password storage: a server should never be able to tell you your own password. It keeps just enough to check a guess — never enough to recover the original. So at sign-up it doesn't store your password; it stores a one-way, salted, slow hash of it. At login it runs the same transform on what you typed and compares the results.

Done right, a stolen database is a pile of useless fingerprints. Done wrong, it's a spreadsheet of everyone's password.

Why hashing alone isn't enough

“Just hash it” sounds safe — a hash is one-way, after all. But a bare hash is deterministic, and that's the leak. The same password always produces the same digest, which hands an attacker two free wins: identical passwords are visibly identical, and a single rainbow table of common-password hashes reverses them all at once.

Toggle the leaked table below between bare SHA-256 and salted PBKDF2. Watch Alice and Bob — who picked the same password — and watch the rainbow table do its work:

a leaked user table — stored as:
alice

not in the common-password table — yet

bob

not in the common-password table — yet

carol

not in the common-password table — yet

Alice and Bob chose the same password, so their stored hashes are identical — the leak reveals that. And a precomputed table cracks every common password instantly. Bare hashing is barely better than plaintext.

log in as carol — verified, never stored

Rejected — the re-derived hash doesn't match the record.

The server compares hashes, not passwords. It could not tell you carol's password if it tried — it doesn't have it.

Salt, then slow it down

The fix is the previous page's two ideas, applied to storage. A unique salt per user makes every stored hash different — even when two people share a password — so collisions vanish and precomputed tables die. Then a deliberately slow hash (PBKDF2, or better, bcrypt / scrypt / Argon2) means each remaining guess costs real time. An attacker who steals the database still has to brute-force every account separately, slowly.

In the login box above, the server verifies carol's password without ever holding it. It re-derives the hash from her guess and the stored salt, and compares — that's all it can do, and all it should be able to do.

If a site can email you your password, run

A correctly built system cannot recover your password — it only stored a one-way hash. So “here is your password” in an email means they stored it in a form they can read, which means a breach hands it straight to an attacker. Real systems reset passwords (set a new one); they never retrieve the old one.

The hash isn't the last line — it's the last-but-one

Salted slow hashing buys time after a breach; it doesn't make weak passwords strong. A user who picks 123456 is still cracked quickly, salt or no salt. That's why the modern stack adds multi-factor authentication and breach monitoring on top — defense in depth, not a single clever hash.

What it's for

  • Every login you've ever made — this is the standard server-side handling of account passwords.
  • API keys & tokens at rest — the same store-the-hash pattern lets a service verify a secret it never keeps in readable form.
  • Anywhere you verify a secret you shouldn't hold — the goal is always to prove knowledge without storing the thing itself.