Key Derivation Functions
debt(d5/e3/b3/t7)
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.
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).
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.
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.
Also Known As
TL;DR
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
Why It Matters
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
// 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 */ }
// 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);
}
}