Safeguard
Cryptography

Cryptographic salt vs pepper

What is a cryptographic salt, and how does it differ from pepper in password hashing? A technical breakdown of salted hashes, bcrypt salt rounds, and best practices.

Aman Khan
AppSec Engineer
7 min read

A cryptographic salt is a random value generated uniquely for each password and combined with that password before hashing, so that two identical passwords never produce the same stored hash. That single design choice is the answer to what is a cryptographic salt and why every credential store on the internet should use one: it defeats precomputed lookup tables and forces attackers to crack each password individually rather than the whole database at once. Salts are not secret — they're stored alongside the hash in plaintext — but their randomness and uniqueness are what break the economics of mass password cracking.

Salt is frequently confused with "pepper," a related but distinct concept, and both are often discussed alongside bcrypt's cost factor and modern password hashing best practices. This glossary entry breaks down what each term actually means, how they differ, and how a salted hash is built and verified in real systems.

What Is a Cryptographic Salt?

A cryptographic salt is a random, non-secret string generated fresh for every password and prepended or appended to it before the combined value is run through a hash function. The purpose is uniqueness, not secrecy. If two users both choose the password "Summer2024!", an unsalted hash function like plain SHA-256 produces identical output for both — a pattern attackers exploit with rainbow tables, which are precomputed dictionaries mapping billions of common passwords to their hashes. Add a unique 16-byte salt per user, and those two identical passwords produce two completely different hashes, because the hash function is now operating on "Summer2024!" plus a different random string each time.

This is precisely why the 2012 LinkedIn breach was so damaging: the company had stored 6.5 million passwords as unsalted SHA-1 hashes. Researchers cracked the vast majority within days because identical passwords shared identical hashes and precomputed tables already existed for common ones. A modern implementation generates the salt using a cryptographically secure random number generator (never a counter or timestamp), stores it in the same database row as the hash, and regenerates a new salt every time a password is created or changed.

What Is the Difference Between Salt vs Pepper in Password Hashing?

The difference is that salt is unique per-password and stored openly, while pepper is a single secret value shared across all passwords and stored separately from the database, typically in an application secret manager or hardware security module. In salt vs pepper password hashing schemes, salt defends against rainbow tables and duplicate-hash detection, while pepper adds a second layer that only helps if the database is compromised but the application's secret store is not.

A practical example: a web application hashes passwords as bcrypt(password + salt + pepper). The salt is generated per-user and sits in the users table next to the hash — anyone with database access can read it. The pepper, by contrast, is a single application-wide constant (or an HMAC key) stored in a secrets vault like AWS Secrets Manager or HashiCorp Vault, never in the same location as the hashes. If an attacker exfiltrates the database via SQL injection but doesn't compromise the application server or vault, they get the salts for free but not the pepper — so offline cracking attempts fail even against weak passwords. Pepper is a defense-in-depth layer, not a replacement for salting, and it introduces its own operational complexity around key rotation and recovery.

How Is a Salted Hash Constructed and Verified?

A salted hash is constructed by generating a random salt, combining it with the plaintext password, running the result through a slow hash function, and storing the salt and hash together — verification simply repeats the same steps and compares outputs. To walk through a salted hash explained step by step: when a user registers, the server generates a random salt (say, 16 bytes from /dev/urandom or a language's CSPRNG), concatenates it with the submitted password, and feeds that into a hashing algorithm such as bcrypt or Argon2. The output — often the salt and hash bundled into one encoded string, as bcrypt does with its $2b$12$... format — is what gets written to the database.

On login, the server reads the stored value, extracts the embedded salt, recombines it with the password the user just typed, runs the same hash function, and checks whether the result matches the stored hash. No decryption ever happens — hashing is one-way by design. This is why password resets, not password retrieval, are the only recovery path when a user forgets a credential: there is nothing to reverse.

What Are Bcrypt Salt Rounds and Why Do They Matter?

Bcrypt salt rounds (also called the cost factor) determine how many times the underlying Blowfish-based algorithm iterates internally, and increasing them exponentially raises the computational cost of both legitimate logins and brute-force attacks. Bcrypt's work factor is expressed as a power of two — a cost of 10 means 2^10 (1,024) iterations, while a cost of 12 means 2^12 (4,096) iterations. Each increment roughly doubles the time needed to compute a single hash.

This matters because hardware gets faster every year, and a cost factor that was safe in 2015 may be crackable at scale in 2026 using modern GPU clusters. Dropbox, for instance, has publicly discussed layering bcrypt with an additional encryption step precisely because bcrypt's fixed 72-byte input limit and aging design benefit from extra hardening as attacker capabilities grow. Most current guidance recommends a bcrypt cost factor of at least 12, tuned so that a single hash computation takes roughly 200-300 milliseconds on production hardware — fast enough for real users logging in, slow enough to make guessing billions of passwords impractical. Argon2id, the winner of the 2015 Password Hashing Competition, is now preferred over bcrypt for new systems because it's tunable across memory, time, and parallelism cost, making it more resistant to GPU and ASIC-based cracking.

What Are Password Hashing Best Practices Beyond Salting?

Password hashing best practices extend well beyond adding a salt: they include choosing a memory-hard algorithm, tuning cost parameters to current hardware, enforcing minimum password entropy, and never rolling your own crypto. Concretely, that means using Argon2id or bcrypt (never MD5 or unsalted SHA-family functions), generating a cryptographically random salt of at least 16 bytes per credential, storing salts and pepper separately, rotating the pepper on a defined schedule without requiring a full password reset, and rate-limiting authentication endpoints to slow online guessing attempts that salting alone doesn't stop.

Equally important is defense in layers: multi-factor authentication reduces the blast radius of a cracked hash, and monitoring for credential-stuffing patterns catches attackers reusing passwords leaked from other breaches. The 2013 Adobe breach is a cautionary tale on a related front — Adobe used a shared encryption key with predictable padding instead of proper per-user salted hashing, allowing researchers to identify matching passwords across accounts purely from ciphertext patterns, without ever cracking a single hash. The lesson: salting must be implemented correctly, with true per-password randomness, not as a token gesture.

How Safeguard Helps

Safeguard's software supply chain security platform scans codebases, container images, and CI/CD pipelines for exactly these kinds of authentication weaknesses before they reach production — flagging hardcoded pepper values, unsalted or weakly-salted hashing calls, deprecated algorithms like MD5 or unsalted SHA-1, and bcrypt configurations with cost factors too low for current threat models. Rather than relying on manual code review to catch a missing salt parameter buried in a dependency or an internal auth library, Safeguard's static analysis identifies these patterns automatically across every service in your supply chain, correlates them with known CVEs in third-party crypto libraries, and surfaces prioritized findings so security and engineering teams can remediate weak password storage before attackers ever get the chance to exploit it.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.