← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

Password Peppering

security OWASP A2:2021 PHP 5.5+ Intermediate

Also Known As

pepper server-side secret secret salt

TL;DR

A secret server-side value mixed into passwords before hashing — database theft alone is insufficient; the pepper must also be compromised.

Explanation

A pepper is a secret value (distinct from a salt) that is the same for all users, stored outside the database (environment variable, secrets manager, HSM) and concatenated with the password before hashing. If an attacker steals the database, they have hashes and salts but not the pepper — offline cracking is impossible without it. In PHP, prepend or HMAC the pepper before password_hash(): $peppered = hash_hmac('sha256', $password, $pepper); password_hash($peppered, PASSWORD_ARGON2ID). Rotate peppers by re-hashing on next login.

Watch Out

Never store the pepper in the database alongside the hashes — it must live in a separate, access-controlled location to provide any benefit.

Common Misconception

A pepper stored in config is as secure as a salt stored in the database. That is the point — a pepper is a server-side secret that remains useful even if the database is fully dumped, since cracking requires both the hash and the pepper.

Why It Matters

A pepper is a secret added to passwords before hashing — even if the database is stolen, the attacker cannot crack hashes without also compromising the pepper stored outside the database.

Common Mistakes

  • Storing the pepper in the database alongside the hashes — defeats the entire purpose.
  • Using a hardcoded pepper in source code that is committed to version control.
  • Using HMAC-then-hash rather than hash(password + pepper) — the order and method matters for security properties.
  • Not planning for pepper rotation — changing the pepper requires re-hashing all passwords at next login.

Code Examples

💡 Note
Pepper + bcrypt/Argon2id is defence in depth: bcrypt protects against brute force; pepper protects against DB dumps.
✗ Vulnerable
// Pepper stored in the same DB as hashes:
$pepper = $db->query('SELECT value FROM config WHERE key = "pepper"');
$hash = password_hash($password . $pepper, PASSWORD_BCRYPT);
// Database breach reveals both the hash and the pepper
✓ Fixed
// Pepper is an application-level secret added before hashing
// Unlike salt (stored in DB), pepper is in environment config
// A compromised DB dump is useless without the pepper

\$pepper  = \$_ENV['PASSWORD_PEPPER']; // e.g. 64-char random string
\$peppered = hash_hmac('sha256', \$plaintext, \$pepper);
\$hash     = password_hash(\$peppered, PASSWORD_ARGON2ID);

// Verify
\$peppered = hash_hmac('sha256', \$input, \$pepper);
if (password_verify(\$peppered, \$storedHash)) { /* authenticated */ }

// Rotate pepper: add new pepper env var, re-hash on next successful login

Added 15 Mar 2026
Edited 22 Mar 2026
Views 20
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
2 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W 2 pings T 2 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 2 pings F 0 pings S
No pings yet today
Amazonbot 1
Amazonbot 10 Unknown AI 3 Google 2 Perplexity 2 ChatGPT 2 Ahrefs 1
crawler 18 crawler_json 1 pre-tracking 1
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Medium
⚡ Quick Fix
Prepend a secret pepper (from env vars, not the database) to passwords before hashing — if the DB leaks, cracking hashes requires the pepper too; rotate by re-hashing on next login
📦 Applies To
PHP 5.5+ web
🔗 Prerequisites
🔍 Detection Hints
password_hash without pepper; only bcrypt no additional secret; pepper stored in DB alongside password hash
Auto-detectable: ✗ No semgrep
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Low ✗ Manual fix Fix: Medium Context: File Tests: Update
CWE-916 CWE-760

✓ schema.org compliant