Skip to content

Commit 977e418

Browse files
authored
[11.x] handle password_hash() failures better (#53821)
* handle `password_hash()` failures better as of PHP 8.0.0, `password_hash()` no longer returns false on failure, instead a `ValueError` will be thrown if the password hashing algorithm is not valid, or an `Error` if the password hashing failed for an unknown error. I noticed the `ArgonHasher` already had a slightly updated check using error suppression, which was added in #33856. however, I couldn't seem to get that to actually work, and for consistency I updated them both to use try/catch. added tests for all 3 implementations to see if they correctly throw a `RuntimeException` if they are not able to be created. I used invalid "cost" and "time" options to trigger this. not sure if there's a better way. https://www.php.net/manual/en/function.password-hash.php#:~:text=the%20salt%20generation.-,8.0.0,-password_hash()%20no * minor formatting
1 parent ce0e481 commit 977e418

File tree

3 files changed

+35
-12
lines changed

3 files changed

+35
-12
lines changed

src/Illuminate/Hashing/ArgonHasher.php

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Illuminate\Hashing;
44

5+
use Error;
56
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
67
use RuntimeException;
78

@@ -60,13 +61,13 @@ public function __construct(array $options = [])
6061
*/
6162
public function make(#[\SensitiveParameter] $value, array $options = [])
6263
{
63-
$hash = @password_hash($value, $this->algorithm(), [
64-
'memory_cost' => $this->memory($options),
65-
'time_cost' => $this->time($options),
66-
'threads' => $this->threads($options),
67-
]);
68-
69-
if (! is_string($hash)) {
64+
try {
65+
$hash = password_hash($value, $this->algorithm(), [
66+
'memory_cost' => $this->memory($options),
67+
'time_cost' => $this->time($options),
68+
'threads' => $this->threads($options),
69+
]);
70+
} catch (Error) {
7071
throw new RuntimeException('Argon2 hashing not supported.');
7172
}
7273

src/Illuminate/Hashing/BcryptHasher.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Illuminate\Hashing;
44

5+
use Error;
56
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
67
use RuntimeException;
78

@@ -44,11 +45,11 @@ public function __construct(array $options = [])
4445
*/
4546
public function make(#[\SensitiveParameter] $value, array $options = [])
4647
{
47-
$hash = password_hash($value, PASSWORD_BCRYPT, [
48-
'cost' => $this->cost($options),
49-
]);
50-
51-
if ($hash === false) {
48+
try {
49+
$hash = password_hash($value, PASSWORD_BCRYPT, [
50+
'cost' => $this->cost($options),
51+
]);
52+
} catch (Error) {
5253
throw new RuntimeException('Bcrypt hashing not supported.');
5354
}
5455

tests/Hashing/HasherTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,25 @@ public function testIsHashedWithNonHashedValue()
117117
{
118118
$this->assertFalse($this->hashManager->isHashed('foo'));
119119
}
120+
121+
public function testBasicBcryptNotSupported()
122+
{
123+
$this->expectException(RuntimeException::class);
124+
125+
(new BcryptHasher(['rounds' => 0]))->make('password');
126+
}
127+
128+
public function testBasicArgon2iNotSupported()
129+
{
130+
$this->expectException(RuntimeException::class);
131+
132+
(new ArgonHasher(['time' => 0]))->make('password');
133+
}
134+
135+
public function testBasicArgon2idNotSupported()
136+
{
137+
$this->expectException(RuntimeException::class);
138+
139+
(new Argon2IdHasher(['time' => 0]))->make('password');
140+
}
120141
}

0 commit comments

Comments
 (0)