password_verify()
Also Known As
password_verify()
PHP password check
bcrypt verify
TL;DR
Checks a plaintext password against a bcrypt/Argon2 hash produced by password_hash() — the correct way to validate passwords.
Explanation
password_verify($plaintext, $hash) compares a user-supplied password against a stored hash created by password_hash(). It automatically extracts the algorithm, cost, and salt from the stored hash string and is timing-safe. Never compare password hashes with == or === — this is vulnerable to timing attacks and type juggling. password_verify() always returns bool and handles all supported PHP password algorithms transparently.
Common Misconception
✗ password_verify() can be replaced with hash comparison using ===. password_verify() performs a constant-time comparison internally. Using === leaks timing information, and also fails to account for future algorithm upgrades handled automatically by password_needs_rehash().
Why It Matters
password_verify() extracts the algorithm and salt from the stored hash and recomputes it — it is the only correct way to verify a password_hash() result because the salt is embedded.
Common Mistakes
- Comparing hashes with === instead of password_verify() — the embedded salt means the same password produces different hashes.
- Not calling password_needs_rehash() after a successful verify — misses the opportunity to upgrade old hashes.
- Using password_verify() to compare non-password hashes like HMACs — use hash_equals() for those.
- Assuming password_verify() is timing-safe against all attacks — it is, but only use it for password_hash() outputs.
Code Examples
✗ Vulnerable
// Wrong — hashing again and comparing:
$inputHash = password_hash($input, PASSWORD_BCRYPT);
if ($inputHash === $storedHash) { /* Always false — different salts */ }
// Correct:
if (password_verify($input, $storedHash)) { /* Correct */ }
✓ Fixed
// Constant-time, extracts salt from hash automatically
\$hash = \$user->password; // from database
\$input = \$_POST['password'];
if (password_verify(\$input, \$hash)) {
// Authenticated — check if rehash needed
if (password_needs_rehash(\$hash, PASSWORD_ARGON2ID, ['memory_cost' => 65536])) {
\$new = password_hash(\$input, PASSWORD_ARGON2ID);
User::where('id', \$user->id)->update(['password' => \$new]);
}
} else {
// Wrong password — use identical code path as 'user not found' to prevent enumeration
abort(401, 'Invalid credentials');
}
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
15 Mar 2026
Edited
22 Mar 2026
Views
26
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 0
No pings yet today
Amazonbot 1
Amazonbot 10
Perplexity 5
Unknown AI 3
Google 2
Ahrefs 2
Majestic 1
Also referenced
How they use it
crawler 21
crawler_json 1
pre-tracking 1
Related categories
⚡
DEV INTEL
Tools & Severity
🔴 Critical
⚙ Fix effort: Low
⚡ Quick Fix
Use password_verify($input, $hash) — it's constant-time and handles the salt embedded in the bcrypt/argon2 hash automatically; never compare hashes with == or ===
📦 Applies To
PHP 5.5+
web
api
🔗 Prerequisites
🔍 Detection Hints
Hash comparison with === or ==; md5($password) comparison; SHA1 for password storage; no password_verify() usage
Auto-detectable:
✓ Yes
semgrep
phpstan
psalm
⚠ Related Problems
🤖 AI Agent
Confidence: High
False Positives: Low
✓ Auto-fixable
Fix: Low
Context: Line
Tests: Update
CWE-916