password_hash() — Native Bcrypt (PHP 5.5)
debt(d5/e3/b3/t7)
Closest to 'specialist tool catches it' (d5). The detection_hints list phpcs, semgrep, and phpstan — all specialist/SAST tools. The code_pattern 'md5.*password|sha1.*password' means automated detection is possible but requires configuring these tools; it won't be caught by a basic compiler or default linter pass.
Closest to 'simple parameterised fix' (e3). The quick_fix is a pattern replacement — swap MD5/SHA1 calls with password_hash(PASSWORD_ARGON2ID) and add password_needs_rehash() on login. This touches authentication code but is a recognisable, repeatable substitution within one component rather than a cross-cutting architectural change.
Closest to 'localised tax' (b3). Password hashing logic is typically concentrated in one authentication component (registration, login, password reset). Once corrected, the fix doesn't impose an ongoing tax on unrelated parts of the codebase — applies_to is web/cli but the actual usage site is narrow.
Closest to 'serious trap' (t7). The misconception is explicit: developers believe 'MD5 with a salt is secure for passwords,' contradicting the actual security model. MD5/SHA1 are fast hashes used legitimately for checksums elsewhere, so a competent developer who hasn't studied password-specific security will confidently reach for them — a pattern that directly contradicts how hashing should work for passwords.
TL;DR
Explanation
password_hash($password, PASSWORD_BCRYPT) generates a bcrypt hash with automatic salting. password_verify($input, $hash) verifies. PASSWORD_DEFAULT currently means bcrypt but may change — store the full hash string including algorithm prefix. PHP 7.2+ adds PASSWORD_ARGON2I. PHP 7.3+ adds PASSWORD_ARGON2ID (preferred). password_needs_rehash($hash, PASSWORD_DEFAULT) checks if rehashing is needed after algorithm upgrades. Never: MD5/SHA1 for passwords, unsalted hashes, static salts, or reversible encryption for passwords.
Common Misconception
Why It Matters
Common Mistakes
- Using MD5($password) or SHA1($password) — crackable in seconds with rainbow tables.
- Not using password_needs_rehash() during login — fails to upgrade old hashes.
- Truncating passwords before hashing — bcrypt has 72-byte limit in older implementations.
Code Examples
$hash = md5($password); // Crackable instantly
$hash = md5($password . $salt); // Still fast, GPU-crackable
$hash = sha256($password); // Wrong tool — designed for speed
// Hash on registration:
$hash = password_hash($password, PASSWORD_ARGON2ID);
// Verify on login:
if (password_verify($inputPassword, $storedHash)) {
// Upgrade hash if algorithm changed:
if (password_needs_rehash($storedHash, PASSWORD_ARGON2ID)) {
$newHash = password_hash($inputPassword, PASSWORD_ARGON2ID);
// Store $newHash
}
}