All articles
Passwords

Salting and Peppering: Protecting Stored Passwords

Generate Secret Keys team June 4, 2026 7 min read

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