[php-src] Issue #20801: password_needs_rehash() returns true for security downgrades (lower cost/memory)
[email protected] (dg)
| Newsgroups | php.bugs |
|---|---|
| Message-ID | <koBXVcTih7gCdC7YMK66hhPn5r3fYo0MR7TBrviEGQA@main.internal.php.net> |
Issue: https://github.com/php/php-src/issues/20801
Author: dg
### Description
The `password_needs_rehash()` function returns `true` when the new options would **downgrade** security (e.g., lower cost for bcrypt, lower memory_cost for argon2), which can lead to unintentional weakening of password security.
## The following code:
```php
<?php
// Create a hash with high security (cost 12)
$hash = password_hash('test', PASSWORD_BCRYPT, ['cost' => 12]);
echo "Hash created with cost 12: " . substr($hash, 0, 7) . "...\n";
// Check if rehash is needed with LOWER cost (security downgrade)
$needsRehash = password_needs_rehash($hash, PASSWORD_BCRYPT, ['cost' => 5]);
echo "needsRehash (cost 12 -> 5): " . var_export($needsRehash, true) . "\n";
// This is dangerous - application might rehash to weaker security
if ($needsRehash) {
$weakerHash = password_hash('test', PASSWORD_BCRYPT, ['cost' => 5]);
echo "Password downgraded from cost 12 to cost 5!\n";
}
```
## Resulted in this output:
```
Hash created with cost 12: $2y$12$...
needsRehash (cost 12 -> 5): true
Password downgraded from cost 12 to cost 5!
```
## But I expected this output instead:
```
Hash created with cost 12: $2y$12$...
needsRehash (cost 12 -> 5): false
(no downgrade performed)
```
## Expected behavior
`password_needs_rehash()` should return `false` when the new options would **weaken** security compared to the existing hash. It should only return `true` for:
- Algorithm changes
- Security **upgrades** (higher cost, higher memory_cost, etc.)
- Missing/corrupted hashes
## Actual behavior
The function returns `true` for ANY difference in options, including downgrades, which can lead to:
1. Accidental security weakening during configuration changes
2. Rollback/deployment issues where old config downgrades all passwords
3. No protection against misconfiguration
### PHP Version
```plain
This affects all PHP versions and all password algorithms (bcrypt, argon2i, argon2id)
```
### Operating System
_No response_