diff --git a/src/Nexus/Password/Hash/Pbkdf2Hash.php b/src/Nexus/Password/Hash/Pbkdf2Hash.php index 9559331..854b1cb 100644 --- a/src/Nexus/Password/Hash/Pbkdf2Hash.php +++ b/src/Nexus/Password/Hash/Pbkdf2Hash.php @@ -43,6 +43,12 @@ */ private int $length; + /** + * Used on `::verify()` to return early if provided hash's length + * does not match this hasher's instance's supposed hash length. + */ + private int $hashLength; + /** * @param array{ * iterations?: int, @@ -73,6 +79,8 @@ public function __construct( $this->defaultIterations(), self::DEFAULT_LENGTH, ); + + $this->hashLength = \strlen($this->hash('password', salt: random_bytes(16))); } /** @@ -107,6 +115,10 @@ public function verify(string $password, string $hash, string $salt = ''): bool return false; } + if (\strlen($hash) !== $this->hashLength) { + return false; + } + if (str_contains($hash, '$')) { return false; } diff --git a/tests/Password/Hash/Pbkdf2HashTest.php b/tests/Password/Hash/Pbkdf2HashTest.php index e1851e0..d09df47 100644 --- a/tests/Password/Hash/Pbkdf2HashTest.php +++ b/tests/Password/Hash/Pbkdf2HashTest.php @@ -140,7 +140,11 @@ public function testPasswordVerify(): void self::assertFalse($hasher->verify($pass3, $hash, $salt)); self::assertFalse($hasher->verify( $pass2, - Password::fromAlgorithm(Algorithm::Argon2i)->hash($pass2), + substr(Password::fromAlgorithm(Algorithm::Argon2i)->hash($pass2), 0, 40), + )); + self::assertFalse($hasher->verify( + $pass2, + (new Pbkdf2Hash(Algorithm::Pbkdf2HmacSha256, ['length' => 0]))->hash($pass2, salt: $salt), )); } }