framework icon indicating copy to clipboard operation
framework copied to clipboard

Hash::check not applying options before verifying Algorithm

Open GeNyaa opened this issue 7 months ago • 4 comments

Laravel Version

12.x, 11.x

PHP Version

8.2, 8.1

Database Driver & Version

No response

Description

When using;

Hash::check(
    'password',
    'hash',
    [
        'verify' => false,
    ]
);

it'll still verify the algorithm before running the rest of the function and will throw a bcrypt error, if the incorrect value is used.

Steps To Reproduce

make sure bcrypt verify is set to true in config.

Hash::check(
    'password',
    '$2a$12$JU/xbox8TdX8Yp7deAOAnu9KWeGtreepOspbfa4t9x7yaxICVfzuq',
    [
        'verify' => false,
    ]
);

results in an error thrown instead of a true or false.

GeNyaa avatar May 16 '25 18:05 GeNyaa

@GeNyaa If you set it to true in config, that value will be used in constructor

  • https://github.com/laravel/framework/blob/fba3b98d1ff22f75ea4bd70f8200cfa0b6a3f43d/src/Illuminate/Hashing/HashManager.php#L18
  • https://github.com/laravel/framework/blob/fba3b98d1ff22f75ea4bd70f8200cfa0b6a3f43d/src/Illuminate/Hashing/BcryptHasher.php#L38

Image

As you can see above, first check is passed, second check is the hashed password itself. It uses password_get_info to get the algorithm.

  • https://github.com/laravel/framework/blob/fba3b98d1ff22f75ea4bd70f8200cfa0b6a3f43d/src/Illuminate/Hashing/AbstractHasher.php#L13
  • https://github.com/laravel/framework/blob/fba3b98d1ff22f75ea4bd70f8200cfa0b6a3f43d/src/Illuminate/Hashing/BcryptHasher.php#L124

If this the result is not bcrypt it will throw a RuntimeException. I have tried your hashed password and it gives output unknown algorithm.

But I found out that if you pass option when using Hash::check, that options never used. https://github.com/laravel/framework/blob/fba3b98d1ff22f75ea4bd70f8200cfa0b6a3f43d/src/Illuminate/Hashing/AbstractHasher.php#L26

I think it should be like this

public function check(#[\SensitiveParameter] $value, $hashedValue, array $options = [])
{
    if (is_null($hashedValue) || strlen($hashedValue) === 0) {
        return false;
    }

    if (! empty($options)) {
        $this->verifyAlgorithm = $options['verify'] ?? $this->verifyAlgorithm;
    }

    if ($this->verifyAlgorithm && ! $this->isUsingCorrectAlgorithm($hashedValue)) {
        throw new RuntimeException('This password does not use the Bcrypt algorithm.');
    }

    return parent::check($value, $hashedValue, $options);
}

ghabriel25 avatar May 17 '25 12:05 ghabriel25

The hash above is a different bcrypt version 2a instead of 2y, the hash itself should be the same though.

GeNyaa avatar May 18 '25 06:05 GeNyaa

Is the step to reproduce used in any of Laravel documentation? If not, feel free to submit a PR to suggest the new usage.

crynobone avatar May 19 '25 06:05 crynobone

I used verify false as an option to ignore the amount of rounds when checking the password. So it would allow me to check 8, 10 and 12 rounds without turning verify off inside the config/env vars. This doesn't seem documented in the documentation, but is working as expected. However when using 2a version hashes it errors out, which is why I thought this was a bug as it's suppose to just check the hash without verifying version or rounds.

I'll create a pr to address this later, when I have more time on my hands.

GeNyaa avatar May 19 '25 16:05 GeNyaa

Looks like the proposal was rejected. Closing this for now.

crynobone avatar Jul 07 '25 07:07 crynobone