In password storage, a per-user salt, a secret "pepper", and the work factor of the key-derivation function are three separate mechanisms. Explain what attack each one defeats, which of them still helps after a full database dump, and how the pepper must be constructed if you ever want to rotate the secret.
answer
- salt: public, unique, kills precomputation + cross-user amortisation
- salt adds nothing against a targeted single victim
- work factor: the only lever when the attacker has everything
- pepper: secret key, different trust domain, defeats DB-only theft
- pepper as encrypted (reversible) outer layer = rotatable without plaintext
basics
~20 sSalt is unique and public: it kills precomputed tables and stops one guess testing every account. Work factor makes each guess expensive — the only thing that still helps once the attacker holds everything. Pepper is a secret key stored outside the database; it defeats database-only theft, and it is rotatable only if applied as a reversible keyed layer.
solid answer
~50 sThey answer different threat models. **Salt**: unique random value per password, stored in the clear. It removes *amortisation* — precomputed tables, and one hash evaluation testing a candidate against every user at once. It makes no individual password harder; a targeted attack on one user is unaffected. **Work factor**: the cost parameter of the derivation. It is the only mechanism that raises the price of each individual guess, so it is what still bites after a complete compromise. It is bounded by the defender's own latency and CPU/memory budget at peak login concurrency. **Pepper**: a secret held in a different trust domain (key manager or HSM). It is best applied as an *authenticated encryption of the derived value* under that key, storing ciphertext plus a key identifier — because that layer is reversible, rotation is decrypt-under-old-key, re-encrypt-under-new, needing only stored data. It defeats database-only theft and nothing more.
go deeper
Be able to say what each of the three is and name the attack it defeats: salt vs. precomputed tables and cross-user reuse, work factor vs. brute force, pepper vs. a stolen database. Knowing that the salt is public and unique per user is the core recall.
Explain the asymmetry: salt removes amortisation but not per-guess cost; work factor is the only lever once the attacker has the dump; pepper depends on the key and the database being separately compromised. Mention that parameters are stored per record so they can be raised.
Own the construction argument. The pepper should be a reversible keyed outer layer — authenticated encryption of the derived value under a key held in a separate trust domain, stored with a key identifier — so rotation is decrypt-old / re-encrypt-new from stored data alone. Note explicitly that a one-way outer MAC, like mixing the secret into the input, is not rotatable. Also cover work-factor selection under adversarial login load and constant-time comparison after decryption.
Frame it as threat-model economics and key lifecycle: what each control costs the defender, which assumption each one rests on, and what remains true after full application compromise (only the work factor, and only as time). Decide whether the pepper's operational burden — HSM availability on the login path, key rotation jobs, per-row key ids, disaster recovery if the key is lost — is worth the single scenario it covers, and mandate forced credential rotation on any breach regardless.
## Three mechanisms, three threat models Most confusion here comes from judging all three against the same scenario. They answer different questions, and only one of them survives every scenario. ### Salt — removing amortisation A salt is a unique, randomly generated value per password, stored alongside the derived output in the clear. Its job is to make each stored password an independent problem. Without it the attacker gets two enormous economies. *Precomputation*: build a structure mapping candidate passwords to digests once, offline, then resolve any stolen digest by lookup — the cost of the whole exercise is paid before the breach happens. *Amortisation across users*: one evaluation of a candidate tests it against every row in the dump at once, so cracking a hundred million accounts costs what cracking one costs. Salting destroys both, because the work must be redone per row. Note carefully what it does **not** do: it adds no difficulty whatsoever to attacking a single chosen victim. Against a targeted attack the salt buys nothing; only the work factor does. A salt must be unique and unpredictable — per-user random from a cryptographic generator, not a global constant, not the username, not the row id. It is not a secret, and treating it as one leads to storing it somewhere inconvenient for no gain. ### Work factor — the only universal lever The work factor is the parameter that determines how much computation (and, for memory-hard functions, how much memory) one derivation consumes. It is the sole mechanism that raises attacker cost when the attacker has *everything*: the database, the salts, the algorithm, and the parameters. It is chosen from a **defender budget**, not an attacker model, because the defender pays it on every login. The bounding constraint is login latency at peak concurrency: an expensive derivation multiplied by many simultaneous logins is a CPU or memory exhaustion risk, and an attacker can trigger it deliberately by hammering the login endpoint with wrong passwords. So the parameter is picked by measuring on production-class hardware — a small fraction of a second per verification is the usual target — and revisited as hardware improves. That is precisely why the parameters are stored per record: a system that cannot tell which parameters produced a given row cannot raise them safely. ### Pepper — a key, not a salt A pepper is a **secret** value combined with the password material, identical across users (or per tenant), and deliberately stored somewhere the database is not: a key management service, an HSM, a separate secret store, at minimum a configuration source outside the data tier. It defeats exactly one scenario, but a very common one: theft of the database alone — a backup, a replica, an injection-driven dump, a misplaced snapshot. In that scenario offline cracking cannot even begin, because every candidate must be combined with an unknown key. If the attacker instead compromises the application host, they usually obtain the key too and the pepper contributes nothing. **Construction matters, and this is where designs go wrong.** Mixing the secret into the password *before* derivation works, but it welds the secret into the stored value: rotating it would require every user's plaintext password, which you do not have. The better construction applies the secret as a **reversible keyed outer layer**: compute the slow derivation as usual, then *encrypt* that derived value under the pepper key with an authenticated symmetric scheme (an AEAD such as AES-GCM, or a keyed wrap performed inside an HSM), and store the ciphertext plus a key identifier. Verification decrypts the stored value with the current key and compares it, in constant time, against a freshly computed derivation of the submitted password. Rotation is a bulk job that needs **only the stored data**: decrypt under the old key, re-encrypt under the new one. The per-row key identifier lets old and new coexist while the job runs. Contrast this with an outer **MAC** — storing an HMAC of the derived value under the pepper key. That is fine for confidentiality of the hash, but it is **not rotatable**, because the derived value itself is discarded and an HMAC cannot be unwrapped; you cannot compute the MAC under a new key from the MAC under the old one. It has exactly the same rotation problem as mixing the pepper into the input, and the only escapes are chaining MACs under every historical key (so every old key must be kept forever) or re-deriving lazily at each user's next login, which leaves the old key live indefinitely. Reversibility is the property that makes rotation possible; a one-way outer layer destroys it. A pepper is never a substitute for a proper derivation function. It is a layer, not a replacement, and using a secret to justify a fast hash underneath is a bad trade. ## What survives what Against a **precomputed table**, the salt suffices. Against **mass offline cracking of a dump**, only the work factor bites, with the salt ensuring cost is paid per account. Against **theft of the database alone**, the pepper is decisive. Against **full application compromise**, only the work factor remains, and it buys time rather than safety — which is why the response to any credential-store breach is forced rotation and session invalidation regardless of how good the storage was. ## Common design errors Storing the pepper in the same database is the most frequent, and it reduces the pepper to a complicated constant. A single system-wide salt is the second. Fixing work-factor parameters at launch and never revisiting them is the third. And choosing a one-way outer pepper layer while believing it is rotatable is the fourth — the design looks sound right up to the day the key must be retired.
- How do you rotate a pepper without asking every user to reset their password?Apply it as a reversible keyed outer layer: encrypt the slow-derived value under the pepper key with an authenticated scheme (AES-GCM or an HSM wrap) and store the ciphertext with a key identifier. Rotation then decrypts under the old key and re-encrypts under the new one, using only data you already hold, and the key id lets old and new rows coexist while the job runs. A one-way outer layer — an HMAC of the derived value — cannot be rotated this way, because the derived value is discarded and you cannot recompute a MAC under a new key from the old MAC; that leaves only chaining MACs under every historical key or re-deriving at each user's next login. Mixing the secret into the input before derivation has the same problem.
- Where should a pepper be stored, and what breaks if it lives in the same database?In a different trust domain from the password records — a key management service or HSM, or at minimum a secret store the data tier cannot read. If it sits in the same database, any compromise that yields the hashes yields the key, so it degrades to a constant that adds no attacker cost. The whole value of a pepper rests on the assumption that database theft and key theft are separate events.
- How do you choose the work factor in practice?Measure on hardware representative of production and pick the largest cost whose latency and memory footprint are acceptable at peak login concurrency — a fraction of a second per verification is the usual target. Then account for adversarial load: wrong-password floods multiply that cost, so the login path needs rate limiting and the capacity plan must assume the expensive path. Store the parameters with each record so they can be raised later and old rows upgraded on next successful login.
saying these in an interview costs you the question
- Calling the salt secret, or storing it apart from the hash to "protect" it — it is public by design and its only job is removing amortisation.
- Claiming a salt makes each password harder to crack; it does nothing against a targeted attack on one chosen user.
- Believing an HMAC of the derived value under the pepper key can be rotated in bulk — the derived value is gone, so the new MAC cannot be computed from the old one.
- Storing the pepper in the same database (or the same schema) as the hashes, which reduces it to a constant.
- Using a pepper as an excuse to keep a fast hash like SHA-256 underneath instead of a slow key-derivation function.
- Setting the work factor once at launch and not storing the parameters per record, which makes raising them later impossible to do safely.