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

password_hash()

php PHP 5.5+ Beginner
debt(d5/e3/b3/t3)
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 phpstan with a specific code_pattern (md5($_POST['password'] or sha1( in login/registration handlers). This means misuse (using md5/sha1 instead of password_hash) is detectable via SAST tools like semgrep, not by the compiler or a default linter pass, placing it squarely at d5.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix is a direct swap: replace md5/sha1 calls with password_hash($password, PASSWORD_ARGON2ID) and password_verify(). However, existing stored hashes need migration (rehash at next login via password_needs_rehash()), which is a small but contained refactor within the authentication component — not a single-line patch but not cross-cutting either.

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

Closest to 'localised tax' (b3). password_hash() applies to web and cli contexts but its burden is confined to the authentication layer — the password storage column length and the rehash-at-login logic. The rest of the codebase is unaffected. It does impose a persistent check (password_needs_rehash() at login) but this is localised to one component.

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

Closest to 'minor surprise' (t3). The primary misconception documented is that storing the algorithm identifier in the hash string weakens security — this is a subtle but non-catastrophic misunderstanding. The common mistakes (double-hashing, short column truncation, not calling password_needs_rehash()) are real gotchas but are well-documented and typically caught during testing or code review rather than causing silent production failures.

About DEBT scoring →

Also Known As

password_hash() PHP password hashing PASSWORD_DEFAULT

TL;DR

PHP's built-in function for securely hashing passwords using bcrypt or Argon2 with automatic salting.

Explanation

password_hash($password, PASSWORD_BCRYPT) produces a 60-character string that includes the algorithm identifier, cost factor, salt, and hash — everything password_verify() needs to check a login. The salt is generated automatically from the OS CSPRNG. Never store passwords as raw MD5/SHA1; always use password_hash() and pair it with password_verify() for comparison. PASSWORD_ARGON2ID is preferred for new systems.

Common Misconception

Storing the algorithm alongside the hash weakens security. password_hash() embeds the algorithm identifier, cost factor, and salt directly in the hash string — this enables password_needs_rehash() to transparently upgrade hashes when the algorithm changes, without exposing any secret.

Why It Matters

password_hash() handles salt generation, algorithm selection, and formatting automatically — using it correctly means a password stored today remains upgradeable to stronger algorithms tomorrow via password_needs_rehash().

Common Mistakes

  • Passing a pre-hashed password to password_hash() — double-hashing corrupts the input entropy.
  • Storing the result in a column shorter than 255 characters — truncated hashes always fail verification.
  • Using PASSWORD_DEFAULT without checking what algorithm it currently maps to on your PHP version.
  • Not calling password_needs_rehash() at login to upgrade old hashes when algorithm or cost factors change.

Code Examples

✗ Vulnerable
// Pre-hashing before password_hash — entropy loss:
$hash = password_hash(md5($password), PASSWORD_BCRYPT); // md5 first = wrong

// Column too short:
$db->query('CREATE TABLE users (password VARCHAR(60))'); // Must be 255+

// Correct:
$hash = password_hash($password, PASSWORD_ARGON2ID);
✓ Fixed
// Always use password_hash() — never md5/sha1 for passwords
$hash = password_hash($plaintext, PASSWORD_BCRYPT, ['cost' => 12]);
// Or use Argon2id (recommended for new projects, PHP 7.3+)
$hash = password_hash($plaintext, PASSWORD_ARGON2ID, [
    'memory_cost' => 65536, // 64 MB
    'time_cost'   => 4,
    'threads'     => 2,
]);

// Verify
if (password_verify($input, $hash)) { /* authenticated */ }

// Upgrade hash if algorithm/cost has changed
if (password_needs_rehash($hash, PASSWORD_ARGON2ID)) {
    $hash = password_hash($input, PASSWORD_ARGON2ID);
    // save new hash
}

Added 15 Mar 2026
Edited 22 Mar 2026
Views 37
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 1 ping W 0 pings T 2 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 1 ping F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W 1 ping T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 1 ping W 0 pings T 0 pings F
No pings yet today
No pings yesterday
Perplexity 9 Amazonbot 9 Ahrefs 6 Unknown AI 2 Google 1 SEMrush 1 Qwen 1
crawler 29
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Low
⚡ Quick Fix
Use password_hash($password, PASSWORD_ARGON2ID) and password_verify($input, $hash) — never implement your own hashing
📦 Applies To
PHP 5.5+ web cli
🔗 Prerequisites
🔍 Detection Hints
md5($_POST['password'] or sha1( in login or registration handler
Auto-detectable: ✓ Yes semgrep phpstan
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Low ✓ Auto-fixable Fix: Low Context: Line Tests: Update
CWE-916 CWE-759

✓ schema.org compliant