Laravel 9 rejects valid password hashed with bcrypt
P粉270842688
P粉270842688 2024-03-28 17:22:24
0
1
530

I've spent a few days troubleshooting some password validation failures in Laravel 9. Password testperson resolves to hash $2y$10$5xc/wAmNCKV.YhpWOfyNoetCj/r3Fs5TyAskgZuIF/LEItWfm7rPW. A direct query of the corresponding database table confirms that this is the correct hash value. However Laravel's authentication infrastructure rejects this password and refuses authentication.

This is not common. I have multiple passwords that parse correctly. For example, the password eo resolves to $2y$10$uNWYvMVmagIwQ2eXnVKLCOAK1QFQdcRtxbvlghf.Xpg0U1w.N./N2, and Laravel validates the password. The same mechanism creates both user records, although they have different permissions (indicated by a boolean value on the record).

I found a bug in the function password_verify, which was determined to be returning false negatives in this Stack Overflow question and this Treehouse thread.

Specifically, this is the stack in Laravel where this failure point occurs:

  • login The route calls \Illuminate\Foundation\Auth\AuthenticatesUsers::login via the controller class.
  • login Method calls \Illuminate\Foundation\Auth\AuthenticatesUsers::attemptLogin.
  • The
  • attemptLogin method calls the attempt method of the controller guard object.
  • \Illuminate\Auth\SessionGuard::attempt Calls \Illuminate\Auth\SessionGuard::hasValidCredentials.
  • \Illuminate\Auth\SessionGuard::hasValidCredentials Calls the validateCredentials method on the guard provider object.
  • Illuminate\Auth\EloquentUserProvider::validateCredentials Call the check method on its hasher object.
  • Illuminate\Hashing\HashManager::check Calls the check method on its driver.
  • Illuminate\Hashing\BcryptHasher::check Calls Illuminate\Hashing\AbstractHasher::check.
  • Illuminate\Hashing\AbstractHasher::check Calls password_verify.

After unwinding the entire stack, I run the following code in the login method of the login controller:

$provider = $this->guard()->getProvider();
$credentials =  $this->credentials($request);
$user = $provider->retrieveByCredentials($credentials);
$password_unhashed = $request['password'];
$password_hashed = $user->getAuthPassword();
$password_verify = password_verify($password_unhashed, $password_hashed);
logger('attemping login', compact('password_verify','password_unhashed','password_hashed'));

Dump this context:

{
"password_verify": false,
"password_unhashed": "testperson",
"password_hashed": "yxc/wAmNCKV.YhpWOfyNoetCj/r3Fs5TyAskgZuIF/LEItWfm7rPW"
}

If I put that password into a SELECT users WHERE password= query, I get the users I expect.

How is this going? How can I solve this problem?

P粉270842688
P粉270842688

reply all(1)
P粉464082061

I think your assertion that the hash you provided is the "testperson" hash is actually false. Since hashes are one-way, I can't tell you where the hash shown comes from. NOTE: It works on PHP 7.4, but I don't think it will work on PHP 8 and above since the salt option in passwd_hash() is deprecated.

 10, "salt" => substr($testhash, 7, 22));
$pwhash = password_hash($password, PASSWORD_BCRYPT, $options);
echo $pwhash."\n";
$salt = substr($pwhash, 0, 29);
echo $salt."\n";
$cryptpw = crypt($password, $salt);
echo $cryptpw."\n";
if (password_verify($password, $cryptpw)) {
  echo("Verified.\n");
} else  {
  echo("NOT Verified.\n");
}
if (password_needs_rehash($cryptpw, PASSWORD_BCRYPT, $options)) {
  echo("Needs rehash.\n");
} else {
  echo("Doesn't need rehash.\n");
}

/*
testperson results...
yxc/wAmNCKV.YhpWOfyNoeVNPMEcYrxepQeFAssFoAaIYs4WLmgZO
yxc/wAmNCKV.YhpWOfyNoe
yxc/wAmNCKV.YhpWOfyNoeVNPMEcYrxepQeFAssFoAaIYs4WLmgZO
Verified.
Doesn't need rehash.

eo results...
y$uNWYvMVmagIwQ2eXnVKLCOAK1QFQdcRtxbvlghf.Xpg0U1w.N./N2
y$uNWYvMVmagIwQ2eXnVKLCO
y$uNWYvMVmagIwQ2eXnVKLCOAK1QFQdcRtxbvlghf.Xpg0U1w.N./N2
Verified.
Doesn't need rehash.
*/
?>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template