Cryptography

What Is a Salt and Why It Matters for Password Security

8 min read
By
What Is a Salt and Why It Matters for Password Security

Photo by Pixabay from Pexels

If you have ever looked at a stored password hash and wondered why two users with the same password have completely different hashes, the answer is the salt. A salt is one of the most important concepts in password security, and it is also one of the simplest.

Let's look at what a salt is, what it does, and why it matters.

What Is a Salt?

A salt is a random string of bytes that is combined with a password before hashing. Instead of computing hash(password), you compute hash(salt + password) or hash(password + salt). The salt is generated randomly when the user creates or changes their password, and it is stored alongside the hash.

When the user logs in, the system retrieves the stored salt, combines it with the entered password, hashes the combination, and compares the result to the stored hash. If they match, the password is correct.

The salt is not secret. It is stored in plaintext in the database. Its security value comes from being unique per password, not from being hidden.

What Problem Does a Salt Solve?

Without a salt, every user who chooses the same password gets the same hash. This creates several problems:

**Identical passwords are visible.** If Alice and Bob both choose "summer2026", their hashes are identical. An attacker who steals the database can see that Alice and Bob have the same password without cracking either one. If the attacker cracks Alice's password, they automatically know Bob's password too.

**Rainbow tables work.** A rainbow table is a precomputed list of hash-to-password mappings. Without salts, an attacker can build one rainbow table and use it to crack every hash in the database instantly. The table is computed once and can be reused for any database using the same hash function.

**Parallel cracking is efficient.** Without salts, the attacker can hash a candidate password once and compare it to every hash in the database simultaneously. If they try "password123", they check it against all 1 million users in one operation. With salts, they must hash "password123" with each user's unique salt separately, making the attack 1 million times slower for the same number of guesses.

How a Salt Defeats Rainbow Tables

A rainbow table is precomputed for a specific hash function with no salt (or a fixed salt). The attacker computes hash(password) for billions of passwords and stores the results. When they have a stolen hash, they look it up in the table.

With a unique salt per password, this does not work. The hash is hash(salt + password), not hash(password). The attacker would need to build a separate rainbow table for every possible salt. For a 16-byte salt, there are 2^128 possible salts. Building a rainbow table for even one salt takes significant time and storage. Building one for every possible salt is computationally impossible.

This is why salts do not need to be secret. Even if the attacker knows the salt, they still cannot use a precomputed rainbow table. They must compute the hash for each password with that specific salt, which means doing the work at attack time rather than precomputing it.

How a Salt Prevents Identical-Password Detection

With unique salts, even if Alice and Bob choose the same password, their hashes are different because their salts are different:

- Alice: hash("random_salt_A" + "summer2026") = hash_A - Bob: hash("random_salt_B" + "summer2026") = hash_B

hash_A and hash_B are completely different because the salts are different. An attacker looking at the database cannot tell that Alice and Bob have the same password. And if the attacker cracks Alice's password, they cannot use that knowledge to crack Bob's password. They must independently crack Bob's hash with Bob's salt.

This also means the attacker cannot optimize by checking a candidate password against all hashes at once. Without salts, hashing "summer2026" once lets you check it against every hash in the database. With salts, you must hash "summer2026" with each user's salt separately. For a database of 1 million users, this makes the attack 1 million times slower for the same number of password guesses.

How Salts Work in Bcrypt

Bcrypt handles salting automatically. When you hash a password with bcrypt, it generates a random salt internally and embeds it in the output hash string. The hash string includes the version, cost factor, salt, and hash value all in one self-contained string:

$2b$12$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy

The salt is the 22 characters after the cost factor. When you verify a password, bcrypt extracts the salt from the stored hash, uses it to hash the submitted password, and compares the result. You never need to manage salts yourself.

This is one of the great advantages of bcrypt over manual salting. With manual salting, you need to generate a secure random salt, store it separately from the hash, retrieve it during verification, and combine it correctly with the password. Any mistake in this process can weaken security. Bcrypt handles all of this in one function call.

For a deeper look at how bcrypt's salt works, see our bcrypt salt explained guide. And for the broader context of how bcrypt works, read our bcrypt explained post.

Common Salt Mistakes

**Using a shared salt.** Some systems use a single salt for all passwords (sometimes called a "pepper" when used this way, though a pepper has different semantics). A shared salt does not prevent identical-password detection and does not defeat rainbow tables (the attacker can build one table for the shared salt). Every password must have its own unique salt.

**Using a non-random salt.** Using the username, email, or user ID as the salt is not random. An attacker who knows the salt generation scheme can precompute rainbow tables for known usernames or IDs. The salt must be generated with a cryptographically secure random number generator.

**Using a short salt.** A salt of 4 bytes (32 bits) has only 4 billion possible values. With 1 million users, there is a reasonable chance of two users getting the same salt. A salt of at least 16 bytes (128 bits) ensures collisions are negligible.

**Storing the salt incorrectly.** The salt must be stored alongside the hash so it can be retrieved during verification. If you lose the salt, you cannot verify the password. Bcrypt and Argon2 embed the salt in the hash string, which avoids this problem entirely.

**Thinking the salt adds security through secrecy.** The salt is not a secret key. It does not need to be encrypted or hidden. Its security value comes from uniqueness, not secrecy. If you are relying on the salt being secret, you are using it wrong.

Salt vs Pepper

A pepper is related to but different from a salt. A pepper is a secret value that is shared across all passwords and is not stored in the database. The hash is computed as hash(pepper + salt + password). The salt is stored in the database. The pepper is stored separately (in an environment variable or a separate secrets manager).

The pepper adds a layer of security: if the database is stolen but the pepper is not, the attacker cannot verify any password guesses because they do not know the pepper. This is defense in depth.

However, peppers are less common than salts because they add complexity (key management, potential for the pepper to be lost and lock everyone out) and the benefit is limited to the specific scenario where the database is stolen but the application server is not. Salts are essential. Peppers are optional.

The Bottom Line

A salt is a unique random value added to each password before hashing. It is simple, it is not secret, and it defeats two of the most powerful password cracking techniques: rainbow tables and parallel cracking of identical passwords.

Every password hashing function worth using (bcrypt, scrypt, Argon2id) handles salting automatically. You should never need to manage salts manually. If you are using a hash function that does not handle salts (like raw SHA-256), you should switch to one that does.

To see salting in action, try our password hash generator to hash the same password with different salts and see how the output changes. And for a deep dive into the most popular salted password hashing function, read our bcrypt explained guide.

Frequently Asked Questions

What is a salt in password hashing?

A salt is a random string of bytes that is combined with a password before hashing. For example, if the password is "hunter2" and the salt is "x7Xx9K", the hash is computed as hash("x7Xx9Khunter2") rather than hash("hunter2"). The salt is stored alongside the hash so it can be used during verification. Each password gets its own unique random salt.

Does a salt need to be secret?

No. The salt does not need to be secret. Its purpose is to ensure that identical passwords produce different hashes, not to add secrecy. The salt is stored in plaintext alongside the hash. Even if an attacker knows the salt, they still cannot reverse the hash. The security comes from the salt being unique per password, not from it being hidden.

Why does each password need a unique salt?

A unique salt per password ensures that identical passwords produce different hashes. This prevents an attacker from cracking identical passwords in parallel (if two users have the same password, the attacker must crack each one independently) and makes rainbow tables useless (the attacker would need a separate precomputed table for every possible salt). A shared salt across all passwords does not provide these benefits.

How long should a salt be?

A salt should be at least 16 bytes (128 bits) long. This provides enough randomness that the probability of two users getting the same salt is negligible. Bcrypt uses a 16-byte salt. Argon2 recommends at least 16 bytes. The salt must be generated using a cryptographically secure random number generator, not Math.random() or a sequential counter.

Try NovelCrypt Tools

Experience military-grade encryption for your sensitive data. Create self-destructing messages, encrypt files, or explore our experimental lab tools.

Explore NovelCrypt