A salt in password hashing is a random per-user value that defeats rainbow tables. Learn how salts work, why they are not secret, and how Argon2id uses them.
In 2003, Philippe Oechslin published a paper called "Making a Faster Cryptanalytic Time-Memory Trade-Off" that refined Martin Hellman's 1980 time-memory tradeoff into what we now call rainbow tables. With a precomputed table of a few hundred gigabytes, an attacker could invert unsalted password hashes in seconds. The defense is surprisingly small: a salt in password hashing, a random value generated per user and mixed into the hash input before the function runs.
A salt does not need to be secret. It is stored in plaintext right next to the hash. Its job is narrow but important. It ensures that two users who pick the same password get different stored hashes, and it makes precomputed lookup tables useless because an attacker would need a separate table for every salt.
This post covers what a salt actually does, what it does not do, and how modern password hashing functions like Argon2id and bcrypt apply salts in practice. You can generate a random salt and hash a password with it using the PBKDF2 and Argon2 tool.
A salt is a random string of bytes, typically 16 bytes or longer, generated by a cryptographically secure pseudorandom number generator when a user sets or resets their password. The hashing function receives the password concatenated with the salt as its input. The salt is then stored in plaintext alongside the resulting hash in the user database. It is never meant to be hidden.
Consider two users who both choose the password password123. Without a salt, both produce the identical SHA-256 hash ef92b778bafe771e89245da893af8496d9.... An attacker who dumps the database sees the same hash twice and knows instantly that both users share a password. With a salt, user A gets salt a3f9c1... and user B gets salt 7b2e0d.... Their stored hashes differ completely, even though the passwords are identical. The attacker cannot tell the passwords match without cracking each hash independently.
The salt also forces uniqueness across databases. The same password stored at two different sites, each using its own per-user salts, produces different hashes everywhere. This is why a breach at one service does not trivially reveal matching accounts at another. You can produce per-password salts with the Salt Generator tool to see the mechanism directly.
Rainbow tables are the attack that salts exist to defeat. Martin Hellman described the underlying time-memory tradeoff in his 1980 paper "A Cryptanalytic Time-Memory Trade-Off." Philippe Oechslin refined it in 2003 with a faster chain-merging technique and coined the rainbow table name in his paper "Making a Faster Cryptanalytic Time-Memory Trade-Off". The idea: precompute hashes for a huge dictionary of likely passwords, store them in a compact structure, then invert any captured hash by looking it up in the table.
For unsalted hashes this is devastating. An attacker builds one rainbow table for, say, all 8-character lowercase passwords hashed with SHA-256. That table works against every database in the world that uses unsalted SHA-256. A single 64 GB table can crack millions of hashes in seconds. The precomputation cost is paid once and amortized across every breach.
A salt breaks the amortization. Because each user has a unique salt, the attacker would need a separate rainbow table for every salt value. Building one table per user costs as much as brute-forcing that user's hash directly, which eliminates the entire advantage of precomputation. The salt converts a batch problem into a per-hash problem.
People confuse salt and pepper because both are extra inputs mixed into a password hash, but they serve different purposes and have opposite secrecy requirements.
A salt is per-user and non-secret. Every account gets its own random salt, and that salt is stored in the same database row as the hash. The salt's job is to make each hash unique and to defeat precomputed tables. It does not need to be hidden because its security benefit comes from uniqueness, not from secrecy.
A pepper is a single secret shared across all passwords on a system. It is stored outside the database, typically in an environment variable, a key management service, or a hardware security module. The pepper is secret. If the database is stolen but the pepper is not, the attacker cannot verify guesses without it. A pepper adds a second defense layer: even a full database dump is useless without the separate secret.
The key distinction: a salt is unique per user and public. A pepper is shared across users and secret. Most systems need a salt. A pepper is an optional extra for high-value targets where you can guarantee the secret is stored separately from the database.
Modern password hashing functions all handle salting internally. You do not concatenate the salt yourself in production code. You pass the salt to the function and it returns a self-describing encoded string that contains the salt, the parameters, and the hash together.
The OWASP Password Storage Cheat Sheet (current as of 2026) ranks the options. Argon2id is the first choice, defined in RFC 9106. OWASP recommends a minimum of 19 MiB memory, 2 iterations, and 1 degree of parallelism. Argon2id is memory-hard, meaning it forces the attacker to allocate significant RAM for each guess, which makes GPU and ASIC cracking expensive.
bcrypt is the second choice for legacy systems. It uses a work factor (cost) parameter; OWASP recommends 10 or higher. bcrypt generates its own 16-byte salt internally and embeds it in the output string. One limitation: bcrypt silently truncates passwords longer than 72 bytes, so applications should pre-hash long passwords with a fast hash like SHA-256 before passing them to bcrypt.
PBKDF2 is the third choice, recommended mainly when FIPS-140 compliance is required. It is defined in NIST SP 800-132. OWASP recommends 600,000 iterations using HMAC-SHA-256. PBKDF2 is not memory-hard, so it is cheaper to attack on GPUs than Argon2id or scrypt, but it remains acceptable at high iteration counts.
| Function | Precomputed table defeated? | Per-user unique hash? | Memory-hard? |
|---|---|---|---|
| Unsalted SHA-256 | No | No | No |
| Salted bcrypt | Yes | Yes | No |
| Argon2id (salted) | Yes | Yes | Yes |
You can verify a bcrypt hash against a password using the bcrypt Hash Checker tool.
A salt is not a key, and salting does not slow down the cracking of a single hash. This is a common misconception. If an attacker has one salted hash and wants to recover that one password, the salt does not add any cost. The attacker simply includes the known salt in every guess. The salt is public, so it provides no resistance to a targeted brute-force attack against a single account.
What the salt does is prevent batch precomputation. It stops rainbow tables. It stops an attacker from reusing work across users. If 10,000 users share the password letmein, an attacker without salts cracks one hash and instantly knows all 10,000 passwords. With salts, the attacker must run the cracking effort 10,000 separate times.
A salt also does not add entropy to the password itself. A weak password like 123456 is still weak. The salt protects against precomputed attacks, not against dictionary attacks on individual hashes. That is why salts must be paired with a slow, memory-hard hashing function. The salt removes the precomputation shortcut, and the slow function makes each individual guess expensive. Both defenses are needed; neither is sufficient alone.
The salt is the part of password storage that makes precomputation attacks pointless. It is per-user, non-secret, and cheap to generate. Pair it with a memory-hard function like Argon2id and you have the minimum viable defense against stolen-hash cracking. Salting alone is not enough; the hashing function must also be slow and memory-hard so that each individual guess costs the attacker real time and real RAM. Generate a per-password salt and hash with Argon2id using the PBKDF2 and Argon2 tool.
No. A salt is stored in plaintext next to the hash. Its security benefit comes from being unique per user, not from being hidden. An attacker who knows the salt still cannot use a precomputed rainbow table because they would need a separate table for that specific salt.
A salt is a random value unique to each user and stored in the database. A pepper is a single secret shared across all passwords and stored separately, outside the database. Salts are public and per-user. Peppers are secret and system-wide.
No. A salt does not slow down cracking of an individual hash because the attacker knows the salt and includes it in every guess. Salting prevents precomputed rainbow tables and stops an attacker from reusing work across multiple users who share a password.
A salt should be at least 16 bytes (128 bits) generated by a cryptographically secure random number generator. bcrypt uses a 16-byte salt internally. Argon2id and PBKDF2 typically use 16 bytes or more. The salt must be unique per user; length matters less than uniqueness.
Let the hashing function generate and embed the salt. bcrypt, Argon2id, and PBKDF2 libraries produce a self-describing encoded output that contains the salt, parameters, and hash together. Generating the salt manually risks reusing values or using a weak random generator.
Salt Generator
Generate cryptographic salts for secure password hashing and random number generation.
PBKDF2 / Argon2 Hash Tool
Modern password hashing with PBKDF2 and Argon2 algorithms for secure key derivation.
bcrypt Hash Checker
Verify and analyze bcrypt password hashes for security validation and format checking.
How SHA-256 Works: A Step-by-Step Walkthrough for Developers
SHA-256 is defined in NIST FIPS 180-4. This walkthrough explains padding, message schedule expansion, the 64-round compression function, and why you should never use SHA-256 for passwords.
The Difference Between Encoding, Encryption, and Hashing
Base64 is not encryption. This guide defines encoding, encryption, and hashing precisely, runs the same input through each, and explains when to use which in production systems.
What Is a Cryptographic Nonce? (And Why Reusing One Breaks Everything)
Sony reused a nonce in PS3 code signing and hackers extracted the master key. PuTTY had biased nonces in 2024. Here is what a nonce is and why reusing one destroys cryptographic security.