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
. 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?
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.