8000 [Security] Allow to set a fixed algorithm · symfony/symfony@0641859 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0641859

Browse files
committed
[Security] Allow to set a fixed algorithm
1 parent 54e1d12 commit 0641859

File tree

13 files changed

+192
-64
lines changed

13 files changed

+192
-64
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: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ CHANGELOG
1414
option is deprecated and will be disabled in Symfony 5.0. This affects to cookies
1515
with dashes in their names. For example, starting from Symfony 5.0, the `my-cookie`
1616
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
1817

1918
4.2.0
2019
-----

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

Lines changed: 53 additions & 14 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,32 +537,72 @@ 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);
542-
543540
return [
544-
'class' => 'Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder',
545-
'arguments' => [$config['cost'] ?? 13],
541+
'class' => NativePasswordEncoder::class,
542+
'arguments' => [
543+
$config['time_cost'] ?? null,
544+
(($config['memory_cost'] ?? 0) << 10) ?: null,
545+
$config['cost'] ?? null,
546+
\PASSWORD_BCRYPT,
547+
],
546548
];
547549
}
548550

549551
// Argon2i encoder
550552
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);
553+
if (SodiumPasswordEncoder::isSupported() && !($hasSodiumArgon2id = \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13'))) {
554+
return [
555+
'class' => SodiumPasswordEncoder::class,
556+
'arguments' => [
557+
$config['time_cost'] ?? null,
558+
(($config['memory_cost'] ?? 0) << 10) ?: null,
559+
],
560+
];
561+
}
552562

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.');
563+
if (!\defined('PASSWORD_ARGON2I')) {
564+
if ($hasSodiumArgon2id ?? false) {
565+
throw new InvalidConfigurationException('Algorithm "argon2i" is not available. You should either use "argon2id", downgrade your sodium extension or use a different encoder.');
556566
}
567+
throw new InvalidConfigurationException('Algorithm "argon2i" is not available. You should either install the sodium extension, upgrade to PHP 7.2+ or use a different encoder.');
568+
}
557569

558-
throw new InvalidConfigurationException('Argon2i algorithm is not supported. Install the libsodium extension or use "auto" instead.');
570+
return [
571+
'class' => NativePasswordEncoder::class,
572+
'arguments' => [
573+
$config['time_cost'] ?? null,
574+
(($config['memory_cost'] ?? 0) << 10) ?: null,
575+
$config['cost'] ?? null,
576+
\PASSWORD_ARGON2I,
577+
],
578+
];
579+
}
580+
581+
if ('argon2id' === $config['algorithm']) {
582+
if (($hasSodium = SodiumPasswordEncoder::isSupported()) && \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
583+
return [
584+
'class' => SodiumPasswordEncoder::class,
585+
'arguments' => [
586+
$config['time_cost'] ?? null,
587+
(($config['memory_cost'] ?? 0) << 10) ?: null,
588+
],
589+
];
590+
}
591+
592+
if (!\defined('PASSWORD_ARGON2ID')) {
593+
if (\defined('PASSWORD_ARGON2I') || $hasSodium) {
594+
throw new InvalidConfigurationException(sprintf('Algorithm "argon2id" is not available. You can either use "argon2i", upgrade to PHP 7.3+, %s sodium extension or use a different encoder.', $hasSodium ? 'upgrade your' : 'install the'));
595+
}
596+
throw new InvalidConfigurationException(sprintf('Algorithm "argon2id" is not available. You should either %s sodium extension, upgrade to PHP 7.3+ or use a different encoder.', $hasSodium ? 'upgrade your' : 'install the'));
559597
}
560598

561599
return [
562-
'class' => 'Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder',
600+
'class' => NativePasswordEncoder::class,
563601
'arguments' => [
564-
$config['memory_cost'],
565-
$config['time_cost'],
566-
$config['threads'],
602+
$config['time_cost'] ?? null,
603+
(($config['memory_cost'] ?? 0) << 10) ?: null,
604+
$config['cost'] ?? null,
605+
\PASSWORD_ARGON2ID,
567606
],
568607
];
569608
}

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)
0