Salting and Peppering: Protecting Stored Passwords
If your application stores user passwords, how you store them decides how bad a database breach will be. Done correctly, attackers who steal your entire user table still can't recover the passwords. The recipe has three ingredients: a salt, optionally a pepper, and — most importantly — a deliberately slow hashing algorithm.
Never store plaintext (or a plain hash)
Storing passwords in plaintext is indefensible. But storing a plain SHA-256 hash is barely better, for two reasons: identical passwords produce identical hashes (so patterns leak), and fast hashes let attackers test billions of guesses per second against a stolen database. You need to defeat both problems.
Salt: defeat precomputation
A salt is a unique, random value generated per user and combined with the password before hashing. It does two things:
- Stops rainbow tables. Attackers can't precompute hashes of common passwords, because every user's salt changes the result.
- Hides duplicates. Two users with the same password get completely different stored values.
A salt does not need to be secret — it's stored right next to the hash. It only needs to be unique and random; 16 bytes is a typical size. Modern password-hashing libraries generate and store the salt for you automatically.
Pepper: an extra, secret layer
A pepper is also mixed into the password, but unlike a salt it is a single application-wide secret stored separately from the database — in an environment variable, config service, or HSM. The idea: if only the database leaks but the pepper stays safe, the stolen hashes are far harder to crack. A pepper is a defense-in-depth bonus, not a replacement for salting and slow hashing.
The crucial part: use a slow hash
General-purpose hashes are built to be fast, which is exactly wrong for passwords. Use a purpose-built, deliberately slow and memory-hard algorithm:
- Argon2id — the modern first choice (memory-hard, resists GPU/ASIC attacks).
- scrypt — also memory-hard; a solid alternative.
- bcrypt — battle-tested and widely available.
- PBKDF2 — acceptable where standards require it; use a high iteration count.
These let you tune a "work factor" so each guess costs meaningful time and memory, turning an attacker's billions-per-second into a crawl.
What not to do
- Don't invent your own scheme by chaining fast hashes ("SHA-256 a thousand times" is not Argon2).
- Don't reuse one salt for every user.
- Don't store the pepper in the same place as the hashes.
- Don't cap password length so low that strong passphrases won't fit.
Generate salts here
Need random salts for testing or a non-password use case? The Hashes tab has a one-click salt generator, and the More tab generates standalone salts in hex, Base64, or Base64URL — all created locally in your browser.
Need a random salt? Generate cryptographic salts locally and copy them straight into your tests.
Open the salt generator