"We hash our passwords" is the security equivalent of "we lock the front door." Necessary, and nowhere near sufficient. Without a salt, a stolen hash database can be cracked wholesale.

Ask a team how they store passwords and "we hash them" is meant to end the conversation reassuringly. It shouldn't. Hashing is necessary, but a plain hash — even with a strong algorithm — leaves a password database vulnerable to an attack that cracks not one account but potentially all of them at once. The missing ingredient isn't a better hash function. It's a salt, and understanding why it matters is the difference between a breach that exposes a few weak passwords and one that hands over everyone's.

What a salt is

A salt is deceptively simple. Wikipedia defines it precisely:

"In cryptography, a salt is random data fed as an additional input to a one-way function that hashes data, a password or passphrase. Salting helps defend against attacks that use precomputed tables, by vastly growing the size of table needed for a successful attack."

— Wikipedia, "Salt (cryptography)" (CC BY-SA 4.0)

Random data, mixed into the password before hashing, stored alongside the hash. That's it — and it changes the economics of an attack completely.

The attack a salt defeats

Here's the threat. A hash function is deterministic: the same input always produces the same output. So if a million people use the password summer2024, an unsalted system stores the same hash a million times. An attacker doesn't even need to crack it live — they can use a rainbow table, a giant precomputed dictionary of common passwords and their hashes, and simply look up matches. Steal an unsalted database and huge swathes of it fall instantly, no computation required, because the hashes are just lookups waiting to happen.

What a per-user salt changes

Now give every user their own random salt. The same password summer2024 now hashes to a different value for every user, because each one's salt is mixed in first. Two consequences follow. Precomputed rainbow tables become useless — they'd have to be rebuilt for every individual salt, which defeats the entire point of precomputing. And the attacker loses the ability to crack in bulk: they have to attack each account's hash separately, one salt at a time, turning a single wholesale break into millions of individual jobs. The salt doesn't need to be secret — it's stored right next to the hash — because its job isn't secrecy. Its job is to make every hash unique.

Why identical passwords must never collide

This is the property to internalise: in a well-built system, no two stored password hashes should be identical, even for users who chose the exact same password. If two rows in your password table share a hash, you have no salt (or a shared one), and you've handed an attacker the "these people used the same password" signal plus rainbow-table vulnerability. Unique salts guarantee unique hashes, which is exactly what you want an attacker staring at a stolen database to find: no patterns, no shortcuts, no bulk cracking.

Peppering: the extra layer

A pepper is a salt's secret cousin. Where the salt is stored with the hash, a pepper is a single secret value kept separately — in application config or a hardware module, not in the database. Mixed in alongside the salt, it means that even an attacker who steals the entire password database still lacks the pepper needed to verify guesses, because it never lived in the database at all. It's defence in depth: the salt defeats precomputation, the pepper defeats database-only theft. And underneath all of it, the hash function itself should be a deliberately slow, password-specific one (bcrypt, scrypt, Argon2) rather than a fast general-purpose hash — but that's the algorithm choice; salting is the structural piece people skip.

See the effect for yourself

The concepts click fast when you watch them. Take a hash generator, hash the word password, then hash password with a few random characters prepended — the outputs are completely unrelated, which is the salt's whole trick in one demonstration. Generate those random salt values (and strong test passwords) with a password generator. And to feel the avalanche effect that makes hashes unpredictable, run two nearly-identical inputs through the hasher and compare the outputs in a text diff — a one-character change scrambles the entire result. "We hash our passwords" is the start of a good answer. "We salt every password with a unique value, and pepper on top" is the rest of it.

← All articles