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

Salted Hashing

security CWE-759 OWASP A2:2021 PHP 5.5+ Beginner
debt(d5/e3/b3/t7)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches' (d5), semgrep/psalm/phpstan rules can match md5($salt.$password) or sha1(SALT.$password) patterns, but it won't fail compilation and basic linters miss it.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3), quick_fix says swap manual salted hash for password_hash() — usually localized to auth/user model with a verification migration path for existing hashes.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3), password hashing lives in the auth component; choice doesn't pervade the whole codebase but does require consistent verify logic wherever credentials are checked.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7), misconception explicitly states devs believe a shared salt is acceptable — the 'obvious' DIY salting (global constant, username as salt, fast hash + salt) is wrong in multiple non-obvious ways.

About DEBT scoring →

Also Known As

password salt salting passwords per-user salt

TL;DR

Prepending or appending a unique random value (salt) to each password before hashing, neutralising precomputed rainbow table attacks.

Explanation

A salt is a cryptographically random value generated per-user and stored alongside the hash. Salting means two identical passwords produce completely different hashes, so an attacker who obtains the database cannot use precomputed rainbow tables or spot users with the same password. Modern PHP's password_hash() handles salting automatically — never implement manual salting. The salt does not need to be secret, only unique. bcrypt, Argon2id, and scrypt all incorporate salting internally.

Watch Out

Never generate your own salt and pass it to password_hash() — the function handles this securely. The 'salt' option was deprecated in PHP 7.0 and removed in PHP 8.0.

Common Misconception

Using the same salt for all users is better than no salt at all. A shared salt prevents precomputed rainbow table attacks but still allows an attacker who cracks one hash to instantly crack all hashes with the same password. Salts must be unique per user.

Why It Matters

A salt makes every hash unique — without it, two users with the same password have the same hash, enabling precomputed rainbow table attacks.

Common Mistakes

  • Using a global application-wide salt instead of a per-password unique random salt.
  • Storing the salt in a separate column and not including it in the hash input consistently.
  • Using a predictable salt like the username or user ID — defeats the purpose.
  • Using salted MD5 or SHA1 — these are still fast-hashable; use password_hash() with bcrypt/argon2 instead.

Code Examples

✗ Vulnerable
// Unsalted — rainbow tables crack these instantly
\$hash = sha1(\$password);

// Shared salt — compromise of one reveals all
\$hash = sha1(\$globalSalt . \$password);
✓ Fixed
// PHP generates a unique per-password salt automatically inside password_hash
\$hash = password_hash(\$password, PASSWORD_ARGON2ID);
// The salt is embedded in the hash string — no separate column needed

// Verify — salt extracted automatically from hash string
if (password_verify(\$input, \$hash)) { /* match */ }

// Manual salting (if implementing yourself — rarely necessary)
\$salt = bin2hex(random_bytes(16));       // unique per user
\$hash = hash('sha256', \$salt . \$password);
// Store \$salt alongside \$hash — never store the password

Added 15 Mar 2026
Edited 22 Mar 2026
Views 34
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 1 ping F 0 pings S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 1 ping 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 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 0 pings S 0 pings M 1 ping T 0 pings W 0 pings T 0 pings F
No pings yet today
No pings yesterday
Perplexity 8 Amazonbot 7 Unknown AI 3 SEMrush 3 Google 2 Ahrefs 2
crawler 23 crawler_json 1 pre-tracking 1
DEV INTEL Tools & Severity
🔴 Critical ⚙ Fix effort: Low
⚡ Quick Fix
Use password_hash() which handles salting automatically — never roll your own salted hash; the salt must be unique per-password and stored alongside the hash
📦 Applies To
PHP 5.5+ web api
🔗 Prerequisites
🔍 Detection Hints
md5($salt.$password) manual salted hash; sha1(SALT.$password) global salt constant; hash without password_hash()
Auto-detectable: ✓ Yes semgrep psalm phpstan
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Low ✓ Auto-fixable Fix: Low Context: Line Tests: Update
CWE-759 CWE-760

✓ schema.org compliant