Skip to content
This repository was archived by the owner on May 31, 2024. It is now read-only.

Commit 9c9e5bb

Browse files
feature #34020 [Security] Allow to stick to a specific password hashing algorithm (chalasr)
This PR was merged into the 4.4 branch. Discussion ---------- [Security] Allow to stick to a specific password hashing algorithm | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | Fix #33054 | License | MIT | Doc PR | todo Allows using `argon2i`, `argon2id` and `bcrypt`. Commits ------- 6712d1e504 [Security] Allow to set a fixed algorithm
2 parents a4ff3be + f2060ce commit 9c9e5bb

File tree

4 files changed

+44
-18
lines changed

4 files changed

+44
-18
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ CHANGELOG
1313
* Marked all dispatched event classes as `@final`
1414
* Deprecated returning a non-boolean value when implementing `Guard\AuthenticatorInterface::checkCredentials()`.
1515
* Deprecated passing more than one attribute to `AccessDecisionManager::decide()` and `AuthorizationChecker::isGranted()`
16+
* Added new `argon2id` encoder, undeprecated the `bcrypt` and `argon2i` ones (using `auto` is still recommended by default.)
1617

1718
4.3.0
1819
-----

Core/Encoder/EncoderFactory.php

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\Security\Core\Encoder;
1313

14+
use Symfony\Component\Security\Core\Exception\LogicException;
15+
1416
/**
1517
* A generic encoder factory implementation.
1618
*
@@ -114,21 +116,20 @@ private function getEncoderConfigFromAlgorithm(array $config): array
114116
],
115117
];
116118

117-
/* @deprecated since Symfony 4.3 */
118119
case 'bcrypt':
119-
return [
120-
'class' => BCryptPasswordEncoder::class,
121-
'arguments' => [$config['cost']],
122-
];
120+
$config['algorithm'] = 'native';
121+
$config['native_algorithm'] = PASSWORD_BCRYPT;
122+
123+
return $this->getEncoderConfigFromAlgorithm($config);
123124

124125
case 'native':
125126
return [
126127
'class' => NativePasswordEncoder::class,
127128
'arguments' => [
128129
$config['time_cost'] ?? null,
129130
(($config['memory_cost'] ?? 0) << 10) ?: null,
130-
$config['cost'] ?? null,
131-
],
131+
$config['cost'] ?? null
132+
] + (isset($config['native_algorithm']) ? [3 => $config['native_algorithm']] : []),
132133
];
133134

134135
case 'sodium':
@@ -140,16 +141,29 @@ private function getEncoderConfigFromAlgorithm(array $config): array
140141
],
141142
];
142143

143-
/* @deprecated since Symfony 4.3 */
144144
case 'argon2i':
145-
return [
146-
'class' => Argon2iPasswordEncoder::class,
147-
'arguments' => [
148-
$config['memory_cost'],
149-
$config['time_cost'],
150-
$config['threads'],
151-
],
152-
];
145+
if (SodiumPasswordEncoder::isSupported() && !\defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
146+
$config['algorithm'] = 'sodium';
147+
} elseif (\defined('PASSWORD_ARGON2I')) {
148+
$config['algorithm'] = 'native';
149+
$config['native_algorithm'] = PASSWORD_ARGON2I;
150+
} else {
151+
throw new LogicException(sprintf('Algorithm "argon2i" is not available. Either use %s"auto" or upgrade to PHP 7.2+ instead.', \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13') ? '"argon2id", ' : ''));
152+
}
153+
154+
return $this->getEncoderConfigFromAlgorithm($config);
155+
156+
case 'argon2id':
157+
if (($hasSodium = SodiumPasswordEncoder::isSupported()) && \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
158+
$config['algorithm'] = 'sodium';
159+
} elseif (\defined('PASSWORD_ARGON2ID')) {
160+
$config['algorithm'] = 'native';
161+
$config['native_algorithm'] = PASSWORD_ARGON2ID;
162+
} else {
163+
throw new LogicException(sprintf('Algorithm "argon2id" is not available. Either use %s"auto", upgrade to PHP 7.3+ or use libsodium 1.0.15+ instead.', \defined('PASSWORD_ARGON2I') || $hasSodium ? '"argon2i", ' : ''));
164+
}
165+
166+
return $this->getEncoderConfigFromAlgorithm($config);
153167
}
154168

155169
return [

Core/Encoder/NativePasswordEncoder.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ final class NativePasswordEncoder implements PasswordEncoderInterface, SelfSalti
2727
private $algo;
2828
private $options;
2929

30-
public function __construct(int $opsLimit = null, int $memLimit = null, int $cost = null)
30+
/**
31+
* @param string|null $algo An algorithm supported by password_hash() or null to use the stronger available algorithm
32+
*/
33+
public function __construct(int $opsLimit = null, int $memLimit = null, int $cost = null, string $algo = null)
3134
{
3235
$cost = $cost ?? 13;
3336
$opsLimit = $opsLimit ?? max(4, \defined('SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE') ? \SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE : 4);
@@ -45,7 +48,7 @@ public function __construct(int $opsLimit = null, int $memLimit = null, int $cos
4548
throw new \InvalidArgumentException('$cost must be in the range of 4-31.');
4649
}
4750

48-
$this->algo = \defined('PASSWORD_ARGON2I') ? max(PASSWORD_DEFAULT, \defined('PASSWORD_ARGON2ID') ? PASSWORD_ARGON2ID : PASSWORD_ARGON2I) : PASSWORD_DEFAULT;
51+
$this->algo = $algo ?? (\defined('PASSWORD_ARGON2I') ? max(PASSWORD_DEFAULT, \defined('PASSWORD_ARGON2ID') ? PASSWORD_ARGON2ID : PASSWORD_ARGON2I) : PASSWORD_DEFAULT);
4952
$this->options = [
5053
'cost' => $cost,
5154
'time_cost' => $opsLimit,

Core/Tests/Encoder/NativePasswordEncoderTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ public function testValidation()
5555
$this->assertFalse($encoder->isPasswordValid($result, 'anotherPassword', null));
5656
}
5757

58+
public function testConfiguredAlgorithm()
59+
{
60+
$encoder = new NativePasswordEncoder(null, null, null, PASSWORD_BCRYPT);
61+
$result = $encoder->encodePassword('password', null);
62+
$this->assertTrue($encoder->isPasswordValid($result, 'password', null));
63+
$this->assertStringStartsWith('$2', $result);
64+
}
65+
5866
public function testCheckPasswordLength()
5967
{
6068
$encoder = new NativePasswordEncoder(null, null, 4);

0 commit comments

Comments
 (0)