Skip to content
Permalink
Browse files

[Security] Allow to set a fixed algorithm

  • Loading branch information...
chalasr authored and nicolas-grekas committed Oct 27, 2019
1 parent 54e1d12 commit 6712d1e5040c5959eafe7641f469fffc8003c093
@@ -209,11 +209,6 @@ Security
* Not implementing the methods `__serialize` and `__unserialize` in classes implementing
the `TokenInterface` is deprecated

SecurityBundle
--------------

* Configuring encoders using `argon2i` or `bcrypt` as algorithm has been deprecated, use `auto` instead.

TwigBridge
----------

@@ -509,7 +509,6 @@ SecurityBundle
changed to underscores.
Before: `my-cookie` deleted the `my_cookie` cookie (with an underscore).
After: `my-cookie` deletes the `my-cookie` cookie (with a dash).
* Configuring encoders using `argon2i` or `bcrypt` as algorithm is not supported anymore, use `auto` instead.

Serializer
----------
@@ -2,8 +2,10 @@ CHANGELOG
=========

4.4.0
-----

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

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

4.2.0
-----
@@ -28,7 +28,6 @@
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder;
use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder;
use Symfony\Component\Security\Core\Encoder\SodiumPasswordEncoder;
use Symfony\Component\Security\Core\User\UserProviderInterface;
@@ -538,34 +537,37 @@ private function createEncoder(array $config)
// bcrypt encoder
if ('bcrypt' === $config['algorithm']) {
@trigger_error('Configuring an encoder with "bcrypt" as algorithm is deprecated since Symfony 4.3, use "auto" instead.', E_USER_DEPRECATED);
$config['algorithm'] = 'native';
$config['native_algorithm'] = PASSWORD_BCRYPT;
return [
'class' => 'Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder',
'arguments' => [$config['cost'] ?? 13],
];
return $this->createEncoder($config);
}
// Argon2i encoder
if ('argon2i' === $config['algorithm']) {
@trigger_error('Configuring an encoder with "argon2i" as algorithm is deprecated since Symfony 4.3, use "auto" instead.', E_USER_DEPRECATED);
if (SodiumPasswordEncoder::isSupported() && !\defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
$config['algorithm'] = 'sodium';
} elseif (\defined('PASSWORD_ARGON2I')) {
$config['algorithm'] = 'native';
$config['native_algorithm'] = PASSWORD_ARGON2I;
} else {
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", ' : ''));
}
if (!Argon2iPasswordEncoder::isSupported()) {
if (\extension_loaded('sodium') && !\defined('SODIUM_CRYPTO_PWHASH_SALTBYTES')) {
throw new InvalidConfigurationException('The installed libsodium version does not have support for Argon2i. Use "auto" instead.');
}
return $this->createEncoder($config);
}
throw new InvalidConfigurationException('Argon2i algorithm is not supported. Install the libsodium extension or use "auto" instead.');
if ('argon2id' === $config['algorithm']) {
if (($hasSodium = SodiumPasswordEncoder::isSupported()) && \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
$config['algorithm'] = 'sodium';
} elseif (\defined('PASSWORD_ARGON2ID')) {
$config['algorithm'] = 'native';
$config['native_algorithm'] = PASSWORD_ARGON2ID;
} else {
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", ' : ''));
}
return [
'class' => 'Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder',
'arguments' => [
$config['memory_cost'],
$config['time_cost'],
$config['threads'],
],
];
return $this->createEncoder($config);
}
if ('native' === $config['algorithm']) {
@@ -574,8 +576,8 @@ private function createEncoder(array $config)
'arguments' => [
$config['time_cost'],
(($config['memory_cost'] ?? 0) << 10) ?: null,
$config['cost'],
],
$config['cost']
] + (isset($config['native_algorithm']) ? [3 => $config['native_algorithm']] : []),
];
}
@@ -18,7 +18,7 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;
use Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder;
use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder;
use Symfony\Component\Security\Core\Encoder\SodiumPasswordEncoder;
abstract class CompleteConfigurationTest extends TestCase
@@ -377,14 +377,9 @@ public function testEncodersWithLibsodium()
]], $container->getDefinition('security.encoder_factory.generic')->getArguments());
}
/**
* @group legacy
*
* @expectedDeprecation Configuring an encoder with "argon2i" as algorithm is deprecated since Symfony 4.3, use "auto" instead.
*/
public function testEncodersWithArgon2i()
{
if (!Argon2iPasswordEncoder::isSupported()) {
if (!($sodium = SodiumPasswordEncoder::isSupported() && !\defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) && !\defined('PASSWORD_ARGON2I')) {
$this->markTestSkipped('Argon2i algorithm is not supported.');
}
@@ -429,19 +424,15 @@ public function testEncodersWithArgon2i()
'arguments' => [8, 102400, 15],
],
'JMS\FooBundle\Entity\User7' => [
'class' => 'Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder',
'arguments' => [256, 1, 2],
'class' => $sodium ? SodiumPasswordEncoder::class : NativePasswordEncoder::class,
'arguments' => $sodium ? [256, 1] : [1, 262144, null, \PASSWORD_ARGON2I],
],
]], $container->getDefinition('security.encoder_factory.generic')->getArguments());
}
/**
* @group legacy
*/
public function testEncodersWithBCrypt()
{
$container = $this->getContainer('bcrypt_encoder');
$this->assertEquals([[
'JMS\FooBundle\Entity\User1' => [
'class' => 'Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder',
@@ -481,8 +472,8 @@ public function testEncodersWithBCrypt()
'arguments' => [8, 102400, 15],
],
'JMS\FooBundle\Entity\User7' => [
'class' => 'Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder',
'arguments' => [15],
'class' => NativePasswordEncoder::class,
'arguments' => [null, null, 15, \PASSWORD_BCRYPT],
],
]], $container->getDefinition('security.encoder_factory.generic')->getArguments());
}
@@ -8,7 +8,6 @@
'algorithm' => 'argon2i',
'memory_cost' => 256,
'time_cost' => 1,
'threads' => 2,
],
],
]);
@@ -10,7 +10,7 @@
</imports>

<sec:config>
<sec:encoder class="JMS\FooBundle\Entity\User7" algorithm="argon2i" memory_cost="256" time_cost="1" threads="2" />
<sec:encoder class="JMS\FooBundle\Entity\User7" algorithm="argon2i" memory_cost="256" time_cost="1" />
</sec:config>

</container>
@@ -7,4 +7,3 @@ security:
algorithm: argon2i
memory_cost: 256
time_cost: 1
threads: 2
@@ -15,8 +15,6 @@
use Symfony\Bundle\SecurityBundle\Command\UserPasswordEncoderCommand;
use Symfony\Component\Console\Application as ConsoleApplication;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder;
use Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder;
use Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder;
@@ -55,9 +53,6 @@ public function testEncodeNoPasswordNoInteraction()
$this->assertEquals($statusCode, 1);
}
/**
* @group legacy
*/
public function testEncodePasswordBcrypt()
{
$this->setupBcrypt();
@@ -70,18 +65,15 @@ public function testEncodePasswordBcrypt()
$output = $this->passwordEncoderCommandTester->getDisplay();
$this->assertStringContainsString('Password encoding succeeded', $output);
$encoder = new BCryptPasswordEncoder(17);
$encoder = new NativePasswordEncoder(null, null, 17, PASSWORD_BCRYPT);
preg_match('# Encoded password\s{1,}([\w+\/$.]+={0,2})\s+#', $output, $matches);
$hash = $matches[1];
$this->assertTrue($encoder->isPasswordValid($hash, 'password', null));
}
/**
* @group legacy
*/
public function testEncodePasswordArgon2i()
{
if (!Argon2iPasswordEncoder::isSupported()) {
if (!($sodium = SodiumPasswordEncoder::isSupported() && !\defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) && !\defined('PASSWORD_ARGON2I')) {
$this->markTestSkipped('Argon2i algorithm not available.');
}
$this->setupArgon2i();
@@ -94,7 +86,28 @@ public function testEncodePasswordArgon2i()
$output = $this->passwordEncoderCommandTester->getDisplay();
$this->assertStringContainsString('Password encoding succeeded', $output);
$encoder = new Argon2iPasswordEncoder();
$encoder = $sodium ? new SodiumPasswordEncoder() : new NativePasswordEncoder(null, null, null, PASSWORD_ARGON2I);
preg_match('# Encoded password\s+(\$argon2i?\$[\w,=\$+\/]+={0,2})\s+#', $output, $matches);
$hash = $matches[1];
$this->assertTrue($encoder->isPasswordValid($hash, 'password', null));
}
public function testEncodePasswordArgon2id()
{
if (!($sodium = (SodiumPasswordEncoder::isSupported() && \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13'))) && !\defined('PASSWORD_ARGON2ID')) {
$this->markTestSkipped('Argon2id algorithm not available.');
}
$this->setupArgon2id();
$this->passwordEncoderCommandTester->execute([
'command' => 'security:encode-password',
'password' => 'password',
'user-class' => 'Custom\Class\Argon2id\User',
], ['interactive' => false]);
$output = $this->passwordEncoderCommandTester->getDisplay();
$this->assertStringContainsString('Password encoding succeeded', $output);
$encoder = $sodium ? new SodiumPasswordEncoder() : new NativePasswordEncoder(null, null, null, PASSWORD_ARGON2ID);
preg_match('# Encoded password\s+(\$argon2id?\$[\w,=\$+\/]+={0,2})\s+#', $output, $matches);
$hash = $matches[1];
$this->assertTrue($encoder->isPasswordValid($hash, 'password', null));
@@ -195,12 +208,9 @@ public function testEncodePasswordNativeOutput()
$this->assertStringNotContainsString(' Generated salt ', $this->passwordEncoderCommandTester->getDisplay());
}
/**
* @group legacy
*/
public function testEncodePasswordArgon2iOutput()
{
if (!Argon2iPasswordEncoder::isSupported()) {
if (!(SodiumPasswordEncoder::isSupported() && !\defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) && !\defined('PASSWORD_ARGON2I')) {
$this->markTestSkipped('Argon2i algorithm not available.');
}
@@ -214,6 +224,22 @@ public function testEncodePasswordArgon2iOutput()
$this->assertStringNotContainsString(' Generated salt ', $this->passwordEncoderCommandTester->getDisplay());
}
public function testEncodePasswordArgon2idOutput()
{
if (!(SodiumPasswordEncoder::isSupported() && \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) && !\defined('PASSWORD_ARGON2ID')) {
$this->markTestSkipped('Argon2id algorithm not available.');
}
$this->setupArgon2id();
$this->passwordEncoderCommandTester->execute([
'command' => 'security:encode-password',
'password' => 'p@ssw0rd',
'user-class' => 'Custom\Class\Argon2id\User',
], ['interactive' => false]);
$this->assertStringNotContainsString(' Generated salt ', $this->passwordEncoderCommandTester->getDisplay());
}
public function testEncodePasswordSodiumOutput()
{
if (!SodiumPasswordEncoder::isSupported()) {
@@ -317,6 +343,19 @@ private function setupArgon2i()
$this->passwordEncoderCommandTester = new CommandTester($passwordEncoderCommand);
}
private function setupArgon2id()
{
putenv('COLUMNS='.(119 + \strlen(PHP_EOL)));
$kernel = $this->createKernel(['test_case' => 'PasswordEncode', 'root_config' => 'argon2id.yml']);
$kernel->boot();
$application = new Application($kernel);
$passwordEncoderCommand = $application->get('security:encode-password');
$this->passwordEncoderCommandTester = new CommandTester($passwordEncoderCommand);
}
private function setupBcrypt()
{
putenv('COLUMNS='.(119 + \strlen(PHP_EOL)));
@@ -0,0 +1,7 @@
imports:
- { resource: config.yml }

security:
encoders:
Custom\Class\Argon2id\User:
algorithm: argon2id
@@ -13,6 +13,7 @@ CHANGELOG
* Marked all dispatched event classes as `@final`
* Deprecated returning a non-boolean value when implementing `Guard\AuthenticatorInterface::checkCredentials()`.
* Deprecated passing more than one attribute to `AccessDecisionManager::decide()` and `AuthorizationChecker::isGranted()`
* Added new `argon2id` encoder, undeprecated the `bcrypt` and `argon2i` ones (using `auto` is still recommended by default.)

4.3.0
-----

0 comments on commit 6712d1e

Please sign in to comment.
You can’t perform that action at this time.