Skip to content

Commit e0da010

Browse files
committed
Passwords: changed from static to object class (BC break)
it triggers E_DEPRECATED automatically (method should not be called statically)
1 parent 8647df3 commit e0da010

File tree

5 files changed

+72
-32
lines changed

5 files changed

+72
-32
lines changed

src/Security/Passwords.php

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,40 @@
1313

1414

1515
/**
16-
* Passwords tools.
16+
* Password Hashing.
1717
*/
1818
class Passwords
1919
{
20-
use Nette\StaticClass;
20+
use Nette\SmartObject;
21+
22+
/** @var int */
23+
private $algo;
24+
25+
/** @var array */
26+
private $options;
27+
2128

2229
/**
23-
* Computes salted password hash. Accepts option 'cost' (4-31)
30+
* See http://php.net/manual/en/password.constants.php
2431
*/
25-
public static function hash(string $password, array $options = []): string
32+
public function __construct(int $algo = PASSWORD_BCRYPT, array $options = [])
2633
{
27-
if (isset($options['cost']) && ($options['cost'] < 4 || $options['cost'] > 31)) {
28-
throw new Nette\InvalidArgumentException("Cost must be in range 4-31, $options[cost] given.");
29-
}
34+
$this->algo = $algo;
35+
$this->options = $options;
36+
}
37+
38+
39+
/**
40+
* Computes salted password hash.
41+
*/
42+
public function hash(string $password): string
43+
{
44+
$hash = isset($this)
45+
? @password_hash($password, $this->algo, $this->options) // @ is escalated to exception
46+
: @password_hash($password, PASSWORD_BCRYPT, func_get_args()[1] ?? []); // back compatibility with v2.x
3047

31-
$hash = password_hash($password, PASSWORD_BCRYPT, $options);
32-
if ($hash === false || strlen($hash) < 60) {
33-
throw new Nette\InvalidStateException('Hash computed by password_hash is invalid.');
48+
if (!$hash) {
49+
throw new Nette\InvalidStateException('Computed hash is invalid. ' . error_get_last()['message']);
3450
}
3551
return $hash;
3652
}
@@ -39,17 +55,19 @@ public static function hash(string $password, array $options = []): string
3955
/**
4056
* Verifies that a password matches a hash.
4157
*/
42-
public static function verify(string $password, string $hash): bool
58+
public function verify(string $password, string $hash): bool
4359
{
4460
return password_verify($password, $hash);
4561
}
4662

4763

4864
/**
49-
* Checks if the given hash matches the options. Accepts option 'cost' (4-31)
65+
* Checks if the given hash matches the options.
5066
*/
51-
public static function needsRehash(string $hash, array $options = []): bool
67+
public function needsRehash(string $hash): bool
5268
{
53-
return password_needs_rehash($hash, PASSWORD_BCRYPT, $options);
69+
return isset($this)
70+
? password_needs_rehash($hash, $this->algo, $this->options)
71+
: password_needs_rehash($hash, PASSWORD_BCRYPT, func_get_args()[1] ?? []); // back compatibility with v2.x
5472
}
5573
}

tests/Security/Passwords.hash().phpt

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,16 @@ require __DIR__ . '/../bootstrap.php';
1414

1515

1616
Assert::truthy(
17-
preg_match('#^\$2.\$\d\d\$.{53}\z#',
18-
Passwords::hash(''))
17+
preg_match('#^\$2.\$\d\d\$.{53}\z#', (new Passwords)->hash(''))
1918
);
2019

2120
Assert::truthy(
22-
preg_match('#^\$2y\$05\$.{53}\z#',
23-
$h = Passwords::hash('dg', ['cost' => 5]))
21+
preg_match('#^\$2y\$05\$.{53}\z#', (new Passwords(PASSWORD_BCRYPT, ['cost' => 5]))->hash('dg'))
2422
);
25-
echo $h;
2623

27-
$hash = Passwords::hash('dg');
24+
$hash = (new Passwords(PASSWORD_BCRYPT))->hash('dg');
2825
Assert::same($hash, crypt('dg', $hash));
2926

30-
31-
Assert::exception(function () {
32-
Passwords::hash('dg', ['cost' => 3]);
33-
}, Nette\InvalidArgumentException::class, 'Cost must be in range 4-31, 3 given.');
34-
3527
Assert::exception(function () {
36-
Passwords::hash('dg', ['cost' => 32]);
37-
}, Nette\InvalidArgumentException::class, 'Cost must be in range 4-31, 32 given.');
28+
(new Passwords(PASSWORD_BCRYPT, ['cost' => 3]))->hash('dg');
29+
}, Nette\InvalidStateException::class, 'Computed hash is invalid. password_hash(): Invalid bcrypt cost parameter specified: 3');

tests/Security/Passwords.needsRehash().phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ use Tester\Assert;
1313
require __DIR__ . '/../bootstrap.php';
1414

1515

16-
Assert::true(Passwords::needsRehash('$2y$05$123456789012345678901uTj3G.8OMqoqrOMca1z/iBLqLNaWe6DK'));
17-
Assert::false(Passwords::needsRehash('$2y$05$123456789012345678901uTj3G.8OMqoqrOMca1z/iBLqLNaWe6DK', ['cost' => 5]));
16+
Assert::true((new Passwords(PASSWORD_BCRYPT))->needsRehash('$2y$05$123456789012345678901uTj3G.8OMqoqrOMca1z/iBLqLNaWe6DK'));
17+
Assert::false((new Passwords(PASSWORD_BCRYPT, ['cost' => 5]))->needsRehash('$2y$05$123456789012345678901uTj3G.8OMqoqrOMca1z/iBLqLNaWe6DK'));
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/**
4+
* Test: Nette\Security\Passwords deprecated static usage
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
use Nette\Security\Passwords;
10+
use Tester\Assert;
11+
12+
13+
require __DIR__ . '/../bootstrap.php';
14+
15+
16+
// deprecated static usage
17+
Assert::error(function () {
18+
Passwords::hash('');
19+
}, E_DEPRECATED, 'Non-static method Nette\Security\Passwords::hash() should not be called statically');
20+
21+
Assert::truthy(
22+
preg_match('#^\$2y\$05\$.{53}\z#', @Passwords::hash('dg', ['cost' => 5])) // @ is not static
23+
);
24+
25+
26+
Assert::true(@Passwords::verify('dg', '$2y$05$123456789012345678901uTj3G.8OMqoqrOMca1z/iBLqLNaWe6DK')); // @ is not static
27+
Assert::false(@Passwords::verify('dgx', '$2y$05$123456789012345678901uTj3G.8OMqoqrOMca1z/iBLqLNaWe6DK')); // @ is not static
28+
29+
30+
Assert::true(@Passwords::needsRehash('$2y$05$123456789012345678901uTj3G.8OMqoqrOMca1z/iBLqLNaWe6DK')); // @ is not static

tests/Security/Passwords.verify().phpt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ use Tester\Assert;
1313
require __DIR__ . '/../bootstrap.php';
1414

1515

16-
Assert::true(Passwords::verify('dg', '$2y$05$123456789012345678901uTj3G.8OMqoqrOMca1z/iBLqLNaWe6DK'));
17-
Assert::true(Passwords::verify('dg', '$2x$05$123456789012345678901uTj3G.8OMqoqrOMca1z/iBLqLNaWe6DK'));
18-
Assert::false(Passwords::verify('dgx', '$2y$05$123456789012345678901uTj3G.8OMqoqrOMca1z/iBLqLNaWe6DK'));
16+
Assert::true((new Passwords)->verify('dg', '$2y$05$123456789012345678901uTj3G.8OMqoqrOMca1z/iBLqLNaWe6DK'));
17+
Assert::true((new Passwords)->verify('dg', '$2x$05$123456789012345678901uTj3G.8OMqoqrOMca1z/iBLqLNaWe6DK'));
18+
Assert::false((new Passwords)->verify('dgx', '$2y$05$123456789012345678901uTj3G.8OMqoqrOMca1z/iBLqLNaWe6DK'));

0 commit comments

Comments
 (0)