"Salt" comes up the moment you talk about password hashing. Adding one random value before hashing changes the security picture dramatically. Here's how it works and the key points (no attack steps).
Why a salt makes passwords safer
Without a salt, users with the same password get the same stored hash. That gives an attacker two options: look up the password directly in a precomputed table (a rainbow table), and reuse the result of cracking one account for everyone with the same password.
Add a salt and the same "password123" stores as something completely different for each user.
For the same reason, a salt must be generated fresh per user (ideally per password). If everyone shares one salt, users with the same password end up with matching stored values again, and most of the benefit is lost. Use a hard-to-predict cryptographic random number generator, and store the salt alongside the hash (it doesn't need to be secret).
A salt only protects against precomputed tables and bulk cracking of reused passwords. Slowing down a brute-force attack on a single password is not its job; that's handled by a deliberately slow hash such as bcrypt/Argon2. They cover different threats, so always use both.
Salt length and how to generate it
Two documents set the baseline:
- NIST SP 800-63B-4 (the US authentication guideline): the salt must be at least 32 bits and chosen so that stored salts rarely collide. Both the salt and the hash are stored for each password
- RFC 9106 (the Argon2 specification): 16 bytes (128 bits) is recommended for password hashing, reducible to 64 bits where space is tight
Three rules for generating it:
- Use a cryptographic random number generator. Use your language's secure random function, not predictable ones like
Math.random()or PHP'smt_rand() - Don't use guessable values: a username, email address, user ID, or sign-up time is not a substitute for a salt
- Generate a new salt when the password changes. Keeping the old one leaves a link between old and new hashes
In practice, standard bcrypt, Argon2, and scrypt implementations meet all of this automatically. If you're writing code that creates salts yourself, treat that as a sign to review it.
Salt vs pepper vs key stretching
Password storage involves three similarly named measures. They defend against different things, so none replaces another.
| Salt | Pepper | Key stretching (slow hashing) | |
|---|---|---|---|
| What it is | A random value per user | A secret value shared across all users | Making the hash computation deliberately expensive |
| Secret? | No (stored with the hash) | Yes (kept outside the database) | Settings don't need to be secret |
| Defends against | Precomputed tables, bulk cracking of identical passwords | Cracking when only the database leaks | Brute force on each hash |
| Implementation | Added automatically by bcrypt/Argon2 | Added by you if needed | bcrypt cost, Argon2 memory and iterations |
NIST SP 800-63B-4 recommends that a "secret key known only to the verifier" (what's usually called a pepper) be stored separately from the password hashes, ideally inside dedicated hardware such as an HSM.
Real case: unsalted storage at LinkedIn (2012)
In June 2012, about 6.5 million password hashes said to belong to LinkedIn users were reported to have been posted online. LinkedIn confirmed that some of them corresponded to LinkedIn accounts and disabled the affected members' passwords.
On its official blog, the company said it had been moving from a system that only hashed passwords to one that both hashed and salted them. At the time, experts quoted in the press criticized the company for protecting passwords with SHA-1 hashing alone.
Two lessons:
- Unsalted hashes let common passwords be recovered in bulk from precomputed tables
- Even mid-migration, if hashes in the old format remain, a leak's impact is decided by that old format
How to check your own environment
Look at the shape of the stored strings
In a development database you control, look at a few password values. For bcrypt, the 22 characters after $2b$12$ are the salt. For Argon2, $argon2id$v=19$m=…,t=…,p=…$ is followed by the salt and the hash separated by $. If the value matches neither shape and there is no separate salt column, the passwords may be stored without a salt.
Create two test accounts with the same password
In a development environment, register two test accounts with the same password and compare the stored values. If they match, the salt isn't working. Also confirm the salt part changes when a password is changed.
Look for code that builds salts by hand
Search the code for the word salt and for places that combine random or hash functions by hand. If you find a predictable random function, a reused username or ID, or a fixed string, replace it with your language or framework's standard password function.
Use the practical guide for migration
How to migrate away from unsalted storage (such as rehashing at login) and the recommended settings for each algorithm are covered in how to store passwords safely.
Common misconceptions vs the truth
Misconception
- A salt must be kept secret
- One shared salt for all users is fine
- A salt makes even weak passwords safe
The truth
- A salt can be public (store it with the hash)
- A different random value per user (ideally per password)
- A salt defends "precomputation and reuse cracking"; brute force is handled by a slow hash
This site's view: get salt and slow hashing right first
A pepper is an extra measure for the case where only the database is stolen. It adds the work of managing a secret somewhere else, and if you lose that secret you can no longer verify anyone's password.
So in practice, the first priority is using salt + a slow hash correctly via bcrypt/Argon2. Consider a pepper once you already have a key-management setup in place.
Sources (primary)
- NIST, "SP 800-63B-4 Digital Identity Guidelines: Authentication and Authenticator Management" (August 2025) — pages.nist.gov (salt of at least 32 bits, storing salt and hash, a separately stored secret key)
- RFC 9106, "Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work Applications" — rfc-editor.org (16-byte salt recommended for password hashing)
- OWASP, "Password Storage Cheat Sheet" — cheatsheetseries.owasp.org (salting, peppering, most libraries handle salts automatically)
- PHC string format (the hash-string format used by Argon2 and others) — github.com/P-H-C
- TechCrunch, "LinkedIn Confirms Hack And Leak Of 'Some' User Passwords" (June 6, 2012) — techcrunch.com (about 6.5 million, the company's statement on moving to hashing and salting)
- Computerworld, "LinkedIn provides breach update -- sort of" (June 8, 2012) — computerworld.com (criticism of SHA-1-only hashing, passwords invalidated)
Read next
- Glossary: what password hashing is (understand it together with salt)
- Learn: how to store passwords safely (hashing + salt in practice)
- Basics: storing passwords the right way