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

Weak Password Hash

Security 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 it' (d5). The detection_hints list semgrep, psalm, and phpstan as tools that can catch the code_pattern (md5($password), sha1($password), etc.). These are specialist SAST/static-analysis tools, not the default compiler or a standard linter, so the issue is not caught instantly but is detectable with deliberate tooling.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix is a direct replacement: swap md5()/sha1()/sha256()/crypt() with password_hash($pass, PASSWORD_ARGON2ID). However, common_mistakes also note needing to add password_needs_rehash() calls on login for upgrading existing hashes, which adds a small but localised refactor beyond a pure one-liner — slightly above e1 but contained within authentication code.

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

Closest to 'localised tax' (b3). The misuse is typically confined to authentication/password-handling code (a specific component), not spread across the entire codebase. While it's a security-critical choice, it doesn't impose a persistent tax on unrelated work streams — the blast radius is limited to login, registration, and password-reset flows.

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

Closest to 'serious trap' (t7). The misconception field explicitly states: 'Adding a salt to MD5 makes it secure' — this directly contradicts how developers reason about cryptographic primitives (salting is taught as the fix for rainbow tables, so it feels sufficient). The trap contradicts established mental models from general cryptography education, leading competent developers who don't specialise in password hashing to guess wrong about the security of salted MD5/SHA1.

About DEBT scoring →

Also Known As

MD5 password SHA1 password hash unsalted hash

TL;DR

Using MD5, SHA-1, or SHA-256 to hash passwords — fast algorithms designed for data integrity, not authentication, crackable in seconds with a GPU.

Explanation

Password hashing requires algorithms designed to be slow: bcrypt, scrypt, or Argon2id. MD5 can hash 10 billion passwords per second on a modern GPU; bcrypt with cost 12 takes ~300ms. A leaked database with MD5-hashed passwords is cracked within hours. PHP's password_hash() with PASSWORD_BCRYPT or PASSWORD_ARGON2ID handles salting, iteration count, and future algorithm upgrades automatically. Never implement password hashing manually.

Common Misconception

Adding a salt to MD5 makes it secure — salting prevents rainbow table attacks but MD5 is still fast enough to brute-force billions of salted hashes per second on commodity hardware.

Why It Matters

A database breach with MD5 or SHA-1 password hashes means every user's password is cracked within hours — with bcrypt or Argon2id it would take centuries.

Common Mistakes

  • md5($password) or sha1($password) — never use these for passwords.
  • sha256($salt . $password) — still fast; use password_hash() instead.
  • Not using password_needs_rehash() — existing bcrypt hashes should be upgraded to Argon2id on next login.
  • Setting bcrypt cost too low — cost 10 is the old default; use 12+ on modern hardware.

Avoid When

  • Never use md5(), sha1(), sha256(), or any general-purpose hash for passwords — they are too fast.
  • Never add a static salt manually — password_hash() generates a cryptographically secure random salt automatically.

When To Use

  • Use password_hash() with PASSWORD_ARGON2ID for all new password storage — it is the current best practice.
  • Call password_needs_rehash() on login to transparently upgrade users from older algorithms.

Code Examples

✗ Vulnerable
// Trivially cracked:
$hash = md5($password);
$hash = sha1($password);
$hash = hash('sha256', $salt . $password); // Still fast

// Verification:
if (md5($input) === $stored) { /* vulnerable */ }
✓ Fixed
// Secure — slow by design:
$hash = password_hash($password, PASSWORD_ARGON2ID, [
    'memory_cost' => 65536,
    'time_cost'   => 4,
    'threads'     => 2,
]);

// Verify:
if (password_verify($input, $stored)) {
    // Upgrade hash if algorithm changed:
    if (password_needs_rehash($stored, PASSWORD_ARGON2ID)) {
        $newHash = password_hash($input, PASSWORD_ARGON2ID);
        updateHash($userId, $newHash);
    }
}

Added 16 Mar 2026
Edited 31 Mar 2026
Views 60
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
1 ping T 0 pings W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 2 pings F 2 pings S 6 pings S 1 ping M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 1 ping 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
No pings yet today
No pings yesterday
Scrapy 9 Perplexity 8 Amazonbot 7 SEMrush 5 Ahrefs 4 ChatGPT 4 Google 3 Bing 3 Unknown AI 2 Claude 1 Meta AI 1
crawler 43 crawler_json 4
DEV INTEL Tools & Severity
🔴 Critical ⚙ Fix effort: Low
⚡ Quick Fix
Replace any md5(), sha1(), sha256(), or crypt() usage for passwords with password_hash($pass, PASSWORD_ARGON2ID) immediately
📦 Applies To
PHP 5.5+ web cli
🔗 Prerequisites
🔍 Detection Hints
md5($password) sha1($password) sha256($password) crypt($password) for password storage or verification
Auto-detectable: ✓ Yes semgrep psalm phpstan
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Low ✓ Auto-fixable Fix: Low Context: Line Tests: Update
CWE-916 CWE-759 CWE-760


✓ schema.org compliant