Guides

What Is a Salt in Password Hashing?

Dr. Anika Rhodesยทยท8 min read

Let me start by killing the most common misconception about salts, because almost every other mistake grows from it: a salt is not a secret. Developers hear "salt" alongside "hash" and "crypto" and assume it must be something to guard, hide, rotate, lock away. It isn't. Open any password database that uses bcrypt and you'll find the salt sitting in plaintext, printed right inside the hash, in full view of anyone who steals the table. That's not a bug. That's the point.

So if a salt is public, what is it actually for? Let's take the myths one at a time and replace each with what a salt provably does โ€” because once you see the single job it's built for, where it helps and where it's useless both become obvious.

What a salt actually is

A salt is a random value generated fresh for each password and mixed in before the password is hashed. Same password, different salt, completely different hash. When you generate a bcrypt hash of the word hunter2 twice, you get two unrelated outputs โ€” because the tool draws a new random salt each time and folds it in before hashing.

Think of it like a locksmith who refuses to fit two houses with the same lock. Even if a hundred people on the street independently choose the identical key shape โ€” the same weak password โ€” the locksmith cuts a unique pin arrangement for each door. One picked lock teaches a burglar nothing about the house next door. That per-door uniqueness, applied to password hashes, is the whole idea of salting.

Myth 1: "A salt has to be kept secret"

This is the big one, so here's the evidence. A bcrypt hash looks like $2b$12$N9qo8uLOickgx2ZMRZoMye... โ€” and the chunk after the cost field is the salt, stored in the clear. It has to be, because when a user logs in, the server needs that exact salt to re-hash the password they typed and check whether the result matches. If the salt were secret and lost, nobody could ever verify a login again.

A salt's strength has nothing to do with being hidden and everything to do with being unique and unpredictable per password. That's a different security property than secrecy, and conflating the two is what sends people down the wrong path โ€” like inventing a scheme to "protect" salts that were never meant to be protected.

Myth 2: "Salting makes each hash harder to crack"

Watch the sleight of hand here, because this myth is half true and that's what makes it dangerous. Salting does not slow down an attacker guessing a single password. If someone has one salted hash and wants to crack it, they grab the salt (it's right there), then try password, 123456, letmein, hashing each with that salt until one matches โ€” exactly as fast as if there were no salt at all.

What a salt actually stops is the economy of scale. Without salts, an attacker precomputes one giant catalog โ€” a rainbow table โ€” mapping common passwords to their hashes, then cracks a million stolen hashes with fast lookups. Salting shatters that catalog: every password now needs its own table, so the precomputation has to be redone per user and the whole shortcut collapses. The burglar's ring of skeleton keys, useless the moment every lock is unique.

So a salt changes the attack from "crack the whole database at once" to "crack one password at a time." That's a massive win โ€” but the per-password cost is set by something else entirely.

The thing that actually does the slowing

If salt gives you uniqueness, what gives you resistance? A deliberately slow hash. This is the real reason bcrypt beats a raw SHA-256 for passwords: SHA-256 is built to be fast, so an attacker with a GPU tries billions of guesses per second. Bcrypt is built to be slow and tunable, via a cost factor that says how many rounds of work each single hash takes.

Bump the cost factor and every guess gets more expensive โ€” for you by a few hundred milliseconds at login, for the attacker by orders of magnitude across billions of guesses. Salt and cost are two independent dials on the same bcrypt generator: salt kills the bulk shortcut, cost taxes each remaining guess. You need both. This is also why hashing a password with SHA-256 alone โ€” even salted โ€” is the wrong tool: it's missing the slowness dial. For deeper background on why bcrypt was designed this way, see bcrypt password hashing explained.

Myth 3: "One salt for the whole app is fine"

Tempting, because it's simpler โ€” store one constant, add it to everything. But a single shared salt reintroduces the exact problem salting exists to solve. If every password uses the same salt, two users with the same password land on the same hash again, and an attacker can build one rainbow table using your one known salt and crack the lot. You've paid for a salt and gotten none of the benefit.

The rule is unambiguous: one fresh, random salt per password, from a cryptographically secure random source (a CSPRNG), at least 16 bytes. In practice you don't do this yourself โ€” call a real password-hashing library and it generates and embeds the salt for you on every hash. The only time you touch a salt directly is inspecting one, and even then a tool like Bcrypt Compare reads the embedded salt out of the stored hash for you when it verifies a password.

Where salt stops, and pepper begins

Here's the honest boundary. Because a salt is public, a stolen database hands the attacker both the hashes and the salts โ€” everything needed to start guessing offline. Salting made that guessing un-parallelizable and slow (with a good algorithm), but it didn't make the database useless on its own.

Closing that last gap takes an actual secret, and that's a pepper: a single value added to every password but kept outside the database โ€” in application config, or a hardware security module. Steal the database alone and, without the pepper, the hashes can't be attacked at all. It's the same escalation you see across crypto: a public value handles the everyday problem, a secret value handles the adversary who's gotten further than you'd like. If you've read what an HMAC is, it's the same instinct โ€” mix in a key to turn a public operation into one only you can reproduce.

The one-line version

A salt is a unique, public random value mixed into a password before hashing, and its single job is to make every hash different so no precomputed catalog can crack many at once. It is not a secret, and it does not slow anything down โ€” a slow algorithm like bcrypt does that. Salt for uniqueness, raise the cost factor for resistance, add a pepper for real secrecy. See all three working together by hashing a password in the Bcrypt Generator and generating a strong one to start with in the Password Generator.

Try the tools

Frequently Asked Questions

What is a salt in simple terms?

A salt is a random string added to a password before it's hashed. Because the salt is different for every password, the resulting hash is unique even when two people pick the exact same password. The salt is saved alongside the hash so the same value can be re-applied when you later verify a login.

Is a salt supposed to be secret?

No. A salt is stored in plaintext next to the hash โ€” bcrypt even packs it into the hash string. Its security doesn't come from being hidden; it comes from being unique per password. Keeping a salt secret buys you nothing, which is exactly why salts are generated fresh rather than memorized or protected.

What's the difference between a salt and a pepper?

A salt is unique per password and stored with the hash, in the open. A pepper is a single secret value added to every password and kept out of the database โ€” in app config or a hardware module. The salt defeats rainbow tables; the pepper means a stolen database alone isn't enough to start cracking.

Does adding a salt make hashing more secure against brute force?

Not directly. A salt stops an attacker from cracking many hashes at once with a precomputed table, but it doesn't slow down guessing a single password. Resistance to brute force comes from a deliberately slow hash โ€” bcrypt, scrypt, or Argon2 โ€” tuned with a cost factor, not from the salt.

How long should a salt be and where do I get one?

16 bytes (128 bits) from a cryptographically secure random generator (a CSPRNG) is the standard. You almost never generate salts by hand: password-hashing libraries like bcrypt create and embed the salt for you automatically each time you hash a password.

DA

Dr. Anika Rhodes writes for CodeUtilityKit, where the team builds free, privacy-first developer tools that run entirely in your browser. Every guide is written and reviewed by developers who use these tools daily.