Skip to content

Commit b5a47df

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 ------- 6712d1e [Security] Allow to set a fixed algorithm
2 parents 60a4d8d + 6712d1e commit b5a47df

File tree

14 files changed

+139
-81
lines changed

14 files changed

+139
-81
lines changed

UPGRADE-4.3.md

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -209,11 +209,6 @@ Security
209209
* Not implementing the methods `__serialize` and `__unserialize` in classes implementing
210210
the `TokenInterface` is deprecated
211211

212-
SecurityBundle
213-
--------------
214-
215-
* Configuring encoders using `argon2i` or `bcrypt` as algorithm has been deprecated, use `auto` instead.
216-
217212
TwigBridge
218213
----------
219214

UPGRADE-5.0.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,6 @@ SecurityBundle
509509
changed to underscores.
510510
Before: `my-cookie` deleted the `my_cookie` cookie (with an underscore).
511511
After: `my-cookie` deletes the `my-cookie` cookie (with a dash).
512-
* Configuring encoders using `argon2i` or `bcrypt` as algorithm is not supported anymore, use `auto` instead.
513512

514513
Serializer
515514
----------

src/Symfony/Bundle/SecurityBundle/CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ CHANGELOG
22
=========
33

44
4.4.0
5+
-----
56

6-
* Deprecated the usage of "query_string" without a "search_dn" and a "search_password" config key in Ldap factories.
7+
* Added new `argon2id` encoder, undeprecated the `bcrypt` and `argon2i` ones (using `auto` is still recommended by default.)
8+
* Deprecated the usage of "query_string" without a "search_dn" and a "search_password" config key in Ldap factories.
79

810
4.3.0
911
-----
@@ -14,7 +16,6 @@ CHANGELOG
1416
option is deprecated and will be disabled in Symfony 5.0. This affects to cookies
1517
with dashes in their names. For example, starting from Symfony 5.0, the `my-cookie`
1618
name will delete `my-cookie` (with a dash) instead of `my_cookie` (with an underscore).
17-
* Deprecated configuring encoders using `argon2i` as algorithm, use `auto` instead
1819

1920
4.2.0
2021
-----

src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
use Symfony\Component\DependencyInjection\Reference;
2929
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
3030
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
31-
use Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder;
3231
use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder;
3332
use Symfony\Component\Security\Core\Encoder\SodiumPasswordEncoder;
3433
use Symfony\Component\Security\Core\User\UserProviderInterface;
@@ -538,34 +537,37 @@ private function createEncoder(array $config)
538537

539538
// bcrypt encoder
540539
if ('bcrypt' === $config['algorithm']) {
541-
@trigger_error('Configuring an encoder with "bcrypt" as algorithm is deprecated since Symfony 4.3, use "auto" instead.', E_USER_DEPRECATED);
540+
$config['algorithm'] = 'native';
541+
$config['native_algorithm'] = PASSWORD_BCRYPT;
542542

543-
return [
544-
'class' => 'Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder',
545-
'arguments' => [$config['cost'] ?? 13],
546-
];
543+
return $this->createEncoder($config);
547544
}
548545

549546
// Argon2i encoder
550547
if ('argon2i' === $config['algorithm']) {
551-
@trigger_error('Configuring an encoder with "argon2i" as algorithm is deprecated since Symfony 4.3, use "auto" instead.', E_USER_DEPRECATED);
548+
if (SodiumPasswordEncoder::isSupported() && !\defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
549+
$config['algorithm'] = 'sodium';
550+
} elseif (\defined('PASSWORD_ARGON2I')) {
551+
$config['algorithm'] = 'native';
552+
$config['native_algorithm'] = PASSWORD_ARGON2I;
553+
} else {
554+
throw new InvalidConfigurationException(sprintf('Algorithm "argon2i" is not available. Either use %s"auto" or upgrade to PHP 7.2+ instead.', \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13') ? '"argon2id", ' : ''));
555+
}
552556

553-
if (!Argon2iPasswordEncoder::isSupported()) {
554-
if (\extension_loaded('sodium') && !\defined('SODIUM_CRYPTO_PWHASH_SALTBYTES')) {
555-
throw new InvalidConfigurationException('The installed libsodium version does not have support for Argon2i. Use "auto" instead.');
556-
}
557+
return $this->createEncoder($config);
558+
}
557559

558-
throw new InvalidConfigurationException('Argon2i algorithm is not supported. Install the libsodium extension or use "auto" instead.');
560+
if ('argon2id' === $config['algorithm']) {
561+
if (($hasSodium = SodiumPasswordEncoder::isSupported()) && \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
562+
$config['algorithm'] = 'sodium';
563+
} elseif (\defined('PASSWORD_ARGON2ID')) {
564+
$config['algorithm'] = 'native';
565+
$config['native_algorithm'] = PASSWORD_ARGON2ID;
566+
} else {
567+
throw new InvalidConfigurationException(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", ' : ''));
559568
}
560569

561-
return [
562-
'class' => 'Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder',
563-
'arguments' => [
564-
$config['memory_cost'],
565-
$config['time_cost'],
566-
$config['threads'],
567-
],
568-
];
570+
return $this->createEncoder($config);
569571
}
570572

571573
if ('native' === $config['algorithm']) {
@@ -574,8 +576,8 @@ private function createEncoder(array $config)
574576
'arguments' => [
575577
$config['time_cost'],
576578
(($config['memory_cost'] ?? 0) << 10) ?: null,
577-
$config['cost'],
578-
],
579+
$config['cost']
580+
] + (isset($config['native_algorithm']) ? [3 => $config['native_algorithm']] : []),
579581
];
580582
}
581583

src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
use Symfony\Component\DependencyInjection\ContainerBuilder;
1919
use Symfony\Component\DependencyInjection\Reference;
2020
use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;
21-
use Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder;
21+
use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder;
2222
use Symfony\Component\Security\Core\Encoder\SodiumPasswordEncoder;
2323

2424
abstract class CompleteConfigurationTest extends TestCase
@@ -377,14 +377,9 @@ public function testEncodersWithLibsodium()
377377
]], $container->getDefinition('security.encoder_factory.generic')->getArguments());
378378
}
379379

380-
/**
381-
* @group legacy
382-
*
383-
* @expectedDeprecation Configuring an encoder with "argon2i" as algorithm is deprecated since Symfony 4.3, use "auto" instead.
384-
*/
385380
public function testEncodersWithArgon2i()
386381
{
387-
if (!Argon2iPasswordEncoder::isSupported()) {
382+
if (!($sodium = SodiumPasswordEncoder::isSupported() && !\defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) && !\defined('PASSWORD_ARGON2I')) {
388383
$this->markTestSkipped('Argon2i algorithm is not supported.');
389384
}
390385

@@ -429,19 +424,15 @@ public function testEncodersWithArgon2i()
429424
'arguments' => [8, 102400, 15],
430425
],
431426
'JMS\FooBundle\Entity\User7' => [
432-
'class' => 'Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder',
433-
'arguments' => [256, 1, 2],
427+
'class' => $sodium ? SodiumPasswordEncoder::class : NativePasswordEncoder::class,
428+
'arguments' => $sodium ? [256, 1] : [1, 262144, null, \PASSWORD_ARGON2I],
434429
],
435430
]], $container->getDefinition('security.encoder_factory.generic')->getArguments());
436431
}
437432

438-
/**
439-
* @group legacy
440-
*/
441433
public function testEncodersWithBCrypt()
442434
{
443435
$container = $this->getContainer('bcrypt_encoder');
444-
445436
$this->assertEquals([[
446437
'JMS\FooBundle\Entity\User1' => [
447438
'class' => 'Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder',
@@ -481,8 +472,8 @@ public function testEncodersWithBCrypt()
481472
'arguments' => [8, 102400, 15],
482473
],
483474
'JMS\FooBundle\Entity\User7' => [
484-
'class' => 'Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder',
485-
'arguments' => [15],
475+
'class' => NativePasswordEncoder::class,
476+
'arguments' => [null, null, 15, \PASSWORD_BCRYPT],
486477
],
487478
]], $container->getDefinition('security.encoder_factory.generic')->getArguments());
488479
}

src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/argon2i_encoder.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
'algorithm' => 'argon2i',
99
'memory_cost' => 256,
1010
'time_cost' => 1,
11-
'threads' => 2,
1211
],
1312
],
1413
]);

src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/argon2i_encoder.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
</imports>
1111

1212
<sec:config>
13-
<sec:encoder class="JMS\FooBundle\Entity\User7" algorithm="argon2i" memory_cost="256" time_cost="1" threads="2" />
13+
<sec:encoder class="JMS\FooBundle\Entity\User7" algorithm="argon2i" memory_cost="256" time_cost="1" />
1414
</sec:config>
1515

1616
</container>

src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/argon2i_encoder.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,3 @@ security:
77
algorithm: argon2i
88
memory_cost: 256
99
time_cost: 1
10-
threads: 2

src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
use Symfony\Bundle\SecurityBundle\Command\UserPasswordEncoderCommand;
1616
use Symfony\Component\Console\Application as ConsoleApplication;
1717
use Symfony\Component\Console\Tester\CommandTester;
18-
use Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder;
19-
use Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder;
2018
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
2119
use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder;
2220
use Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder;
@@ -55,9 +53,6 @@ public function testEncodeNoPasswordNoInteraction()
5553
$this->assertEquals($statusCode, 1);
5654
}
5755

58-
/**
59-
* @group legacy
60-
*/
6156
public function testEncodePasswordBcrypt()
6257
{
6358
$this->setupBcrypt();
@@ -70,18 +65,15 @@ public function testEncodePasswordBcrypt()
7065
$output = $this->passwordEncoderCommandTester->getDisplay();
7166
$this->assertStringContainsString('Password encoding succeeded', $output);
7267

73-
$encoder = new BCryptPasswordEncoder(17);
68+
$encoder = new NativePasswordEncoder(null, null, 17, PASSWORD_BCRYPT);
7469
preg_match('# Encoded password\s{1,}([\w+\/$.]+={0,2})\s+#', $output, $matches);
7570
$hash = $matches[1];
7671
$this->assertTrue($encoder->isPasswordValid($hash, 'password', null));
7772
}
7873

79-
/**
80-
* @group legacy
81-
*/
8274
public function testEncodePasswordArgon2i()
8375
{
84-
if (!Argon2iPasswordEncoder::isSupported()) {
76+
if (!($sodium = SodiumPasswordEncoder::isSupported() && !\defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) && !\defined('PASSWORD_ARGON2I')) {
8577
$this->markTestSkipped('Argon2i algorithm not available.');
8678
}
8779
$this->setupArgon2i();
@@ -94,7 +86,28 @@ public function testEncodePasswordArgon2i()
9486
$output = $this->passwordEncoderCommandTester->getDisplay();
9587
$this->assertStringContainsString('Password encoding succeeded', $output);
9688

97-
$encoder = new Argon2iPasswordEncoder();
89+
$encoder = $sodium ? new SodiumPasswordEncoder() : new NativePasswordEncoder(null, null, null, PASSWORD_ARGON2I);
90+
preg_match('# Encoded password\s+(\$argon2i?\$[\w,=\$+\/]+={0,2})\s+#', $output, $matches);
91+
$hash = $matches[1];
92+
$this->assertTrue($encoder->isPasswordValid($hash, 'password', null));
93+
}
94+
95+
public function testEncodePasswordArgon2id()
96+
{
97+
if (!($sodium = (SodiumPasswordEncoder::isSupported() && \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13'))) && !\defined('PASSWORD_ARGON2ID')) {
98+
$this->markTestSkipped('Argon2id algorithm not available.');
99+
}
100+
$this->setupArgon2id();
101+
$this->passwordEncoderCommandTester->execute([
102+
'command' => 'security:encode-password',
103+
'password' => 'password',
104+
'user-class' => 'Custom\Class\Argon2id\User',
105+
], ['interactive' => false]);
106+
107+
$output = $this->passwordEncoderCommandTester->getDisplay();
108+
$this->assertStringContainsString('Password encoding succeeded', $output);
109+
110+
$encoder = $sodium ? new SodiumPasswordEncoder() : new NativePasswordEncoder(null, null, null, PASSWORD_ARGON2ID);
98111
preg_match('# Encoded password\s+(\$argon2id?\$[\w,=\$+\/]+={0,2})\s+#', $output, $matches);
99112
$hash = $matches[1];
100113
$this->assertTrue($encoder->isPasswordValid($hash, 'password', null));
@@ -195,12 +208,9 @@ public function testEncodePasswordNativeOutput()
195208
$this->assertStringNotContainsString(' Generated salt ', $this->passwordEncoderCommandTester->getDisplay());
196209
}
197210

198-
/**
199-
* @group legacy
200-
*/
201211
public function testEncodePasswordArgon2iOutput()
202212
{
203-
if (!Argon2iPasswordEncoder::isSupported()) {
213+
if (!(SodiumPasswordEncoder::isSupported() && !\defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) && !\defined('PASSWORD_ARGON2I')) {
204214
$this->markTestSkipped('Argon2i algorithm not available.');
205215
}
206216

@@ -214,6 +224,22 @@ public function testEncodePasswordArgon2iOutput()
214224
$this->assertStringNotContainsString(' Generated salt ', $this->passwordEncoderCommandTester->getDisplay());
215225
}
216226

227+
public function testEncodePasswordArgon2idOutput()
228+
{
229+
if (!(SodiumPasswordEncoder::isSupported() && \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) && !\defined('PASSWORD_ARGON2ID')) {
230+
$this->markTestSkipped('Argon2id algorithm not available.');
231+
}
232+
233+
$this->setupArgon2id();
234+
$this->passwordEncoderCommandTester->execute([
235+
'command' => 'security:encode-password',
236+
'password' => 'p@ssw0rd',
237+
'user-class' => 'Custom\Class\Argon2id\User',
238+
], ['interactive' => false]);
239+
240+
$this->assertStringNotContainsString(' Generated salt ', $this->passwordEncoderCommandTester->getDisplay());
241+
}
242+
217243
public function testEncodePasswordSodiumOutput()
218244
{
219245
if (!SodiumPasswordEncoder::isSupported()) {
@@ -317,6 +343,19 @@ private function setupArgon2i()
317343
$this->passwordEncoderCommandTester = new CommandTester($passwordEncoderCommand);
318344
}
319345

346+
private function setupArgon2id()
347+
{
348+
putenv('COLUMNS='.(119 + \strlen(PHP_EOL)));
349+
$kernel = $this->createKernel(['test_case' => 'PasswordEncode', 'root_config' => 'argon2id.yml']);
350+
$kernel->boot();
351+
352+
$application = new Application($kernel);
353+
354+
$passwordEncoderCommand = $application->get('security:encode-password');
355+
356+
$this->passwordEncoderCommandTester = new CommandTester($passwordEncoderCommand);
357+
}
358+
320359
private function setupBcrypt()
321360
{
322361
putenv('COLUMNS='.(119 + \strlen(PHP_EOL)));
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
imports:
2+
- { resource: config.yml }
3+
4+
security:
5+
encoders:
6+
Custom\Class\Argon2id\User:
7+
algorithm: argon2id

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy