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

Key Derivation Functions

Cryptography PHP 5.5+ Advanced
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 and psalm as tools, and the code_pattern shows detectable signatures (md5(), sha1(), sha256() used directly for passwords, or hash() without proper KDF). These are specialist SAST tools rather than default linters or compilers, and the misuse is silent at runtime — no error is thrown when you use SHA-256 for password storage.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix is essentially a function swap: replace md5/sha1/sha256 with password_hash() using Argon2id, and password_verify() for comparison. This is a localised replacement pattern within one component (the auth layer), not a cross-cutting refactor, though it may touch a few files (registration, login, password reset).

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

Closest to 'localised tax' (b3). The choice applies to web and CLI contexts but is typically confined to the authentication/credential-handling component. Once password_hash()/password_verify() is in place, the rest of the codebase is unaffected. The applies_to scope is broad (PHP 5.5+, web, cli) but the actual structural impact is narrow — it doesn't shape every future change.

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

Closest to 'serious trap' (t7). The misconception field directly states the trap: developers believe SHA-256 is a strong password hash because it is a well-known, cryptographically sound algorithm. This contradicts how SHA-256 correctly behaves in other contexts (file integrity, HMAC, TLS), making it a cross-domain confusion — a fast general-purpose hash masquerades as adequate for password storage. The bcrypt silent truncation at 72 characters is an additional notable gotcha compounding the trap.

About DEBT scoring →

Also Known As

PBKDF2 bcrypt scrypt Argon2 password hashing KDF

TL;DR

KDFs (PBKDF2, bcrypt, scrypt, Argon2) derive a cryptographic key from a password by being intentionally slow — making brute-force attacks computationally infeasible.

Explanation

Passwords must not be stored directly — a KDF transforms a password into a hash that cannot be reversed, and is deliberately slow to compute. bcrypt: adaptive cost factor, max 72 chars, PHP's password_hash() default. scrypt: memory-hard (GPU/ASIC resistant) — good for application-level KDFs. Argon2 (Argon2id recommended): winner of Password Hashing Competition, configurable time+memory+parallelism — PHP password_hash() with PASSWORD_ARGON2ID. PBKDF2: NIST-approved, widely supported, less memory-hard than Argon2/scrypt. Never use MD5/SHA (fast — trivially brute-forced).

Common Misconception

SHA-256 is a strong password hash — SHA-256 is a fast hash designed for integrity checking; it can hash billions of passwords per second on a GPU — use Argon2id or bcrypt instead.

Why It Matters

A leaked database with SHA-256 hashed passwords is cracked within hours with a GPU; the same database with Argon2id hashes would take centuries — the algorithm choice is the critical factor.

Common Mistakes

  • MD5 or SHA-1 for passwords — deprecated and trivially cracked; any data breach is catastrophic.
  • bcrypt with passwords > 72 characters — bcrypt silently truncates; use Argon2id for long passwords.
  • Low cost/iteration count — makes hashing fast and brute force feasible; use the highest cost your server tolerates.
  • Not using PHP's password_verify() — manual comparison with == is vulnerable to timing attacks; password_verify() uses constant-time comparison.

Code Examples

✗ Vulnerable
// MD5 password hash — trivially cracked:
$hash = md5($password);          // Cracked in seconds with GPU
$hash = sha256($password);       // Fast — millions/sec on GPU
$hash = md5(md5($password));     // Still fast — do not use

// Manual comparison — timing attack:
if ($storedHash === md5($password)) { /* login */ }
✓ Fixed
// Argon2id — slow, memory-hard, PHP native:
$hash = password_hash($password, PASSWORD_ARGON2ID, [
    'memory_cost' => 65536,  // 64 MB
    'time_cost'   => 4,      // 4 iterations
    'threads'     => 2,
]);

// Verify with constant-time comparison:
if (password_verify($password, $hash)) {
    // Login — rehash if needs_rehash():
    if (password_needs_rehash($hash, PASSWORD_ARGON2ID)) {
        $newHash = password_hash($password, PASSWORD_ARGON2ID);
        updateStoredHash($userId, $newHash);
    }
}

Added 15 Mar 2026
Edited 22 Mar 2026
Views 73
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 1 ping T 1 ping F 1 ping S 3 pings S 3 pings M 0 pings T 1 ping W 2 pings T 0 pings F 0 pings S 0 pings S 1 ping M 1 ping T 0 pings W 0 pings T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Amazonbot 19 Perplexity 9 Scrapy 9 Google 7 Ahrefs 4 SEMrush 4 Majestic 2 Claude 2 Bing 2 Unknown AI 1 Meta AI 1 PetalBot 1
crawler 55 crawler_json 6
DEV INTEL Tools & Severity
🔴 Critical ⚙ Fix effort: Medium
⚡ Quick Fix
Use Argon2id for passwords (password_hash()), PBKDF2 for key derivation from passphrases (hash_pbkdf2()), and HKDF for expanding cryptographic keys — never use a plain hash as a key derivation function
📦 Applies To
PHP 5.5+ web cli
🔗 Prerequisites
🔍 Detection Hints
md5() sha1() sha256() used directly for password storage; hash('sha256', $password) as key without PBKDF2 or Argon2; no iterations/cost factor
Auto-detectable: ✓ Yes semgrep psalm
⚠ 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