Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Security] Argon2i Password Encoder #21604

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 29, 2017
Merged

[Security] Argon2i Password Encoder #21604

merged 1 commit into from
Sep 29, 2017

Conversation

zanbaldwin
Copy link
Member

@zanbaldwin zanbaldwin commented Feb 13, 2017

Q A
Branch? master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets
License MIT
Doc PR WIP

Since the libsodium RFC passed with flying colours, I'd like to kick start a discussion about adding Argon2i as a password encoder to the security component. The initial code proposal in this PR supports both the upcoming public API confirmed for PHP 7.2, and the libsodium PECL extension for those below 7.2 (available for PHP 5.4+).

Concerns

  • Should the test cover hash length? At the moment the result of Argon2i is 96 characters, but because the hashing parameters are included in the result ($argon2i$v=19$m=32768,t=4,p=1$...) this is not guaranteed.
  • I've used one password encoder class because the result should be the same whether running natively in 7.2 or from the PECL extension, but should the logic be split out into separate private methods (like Argon2iPasswordEncoder::encodePassword()) or not (like in Argon2iPasswordEncoder::isPasswordValid())? Since I can't really find anything concrete on Symfony choosing one way over another I'm assuming it's down to personal preference?

The Future

Whilst the libsodium RFC has been approved and the public API confirmed, there has been no confirmation of Argon2i becoming an official algorithm for passhword_hash(). If that is confirmed, then the implementation should absolutely use the native password_* functions since the sodium_* functions do not have an equivalent to the password_needs_rehash() function.

Any feedback would be greatly appreciated 😃

@linaori
Copy link
Contributor

linaori commented Feb 14, 2017

With the constant changing of recommended algorithms, would it be an idea to provide a migration layer in Symfony? Currently people will have to build this themselves, but it could be a lot easier if Symfony would provide an extension point, especially now that we can rehash with native php functions.

@zanbaldwin
Copy link
Member Author

That's a great idea since Symfony really should be promoting security best practices (though likely out of the scope of this PR so I'll have a go at starting that separately - if you've got any ideas on what direction that would take, what the config would look like, etc).

It's pretty much a given that Argon2i will be the next hashing algorithm supported by password_hash() (and therefore password_needs_rehash()) in PHP 7.2 but I haven't actually found anywhere that confirms it - either way it won't become PASSWORD_DEFAULT until at least 7.3 😢

@stof
Copy link
Member

stof commented Feb 14, 2017

is a new password encoder considered a new feature (therefore should be against master) or a security improvement/patch (therefore should be against 2.8)?

to me, it is a new feature

@stof
Copy link
Member

stof commented Feb 14, 2017

Is a MAX_PASSWORD_LENGTH of 72 appropriate? Argon2i is, in terms of execution duration, generally along the lines of BCrypt with a cost of 11.

the max password length of 72 for bcrypt is because the bcrypt algorithm does not support longer passwords at all (it truncates the input at 72 chars).
If Argon2i does not have a 72 chars restriction, we should not add it here (keeping the much longer restriction we have in all other encoders)

@@ -14,6 +14,7 @@
<parameter key="security.encoder.plain.class">Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder</parameter>
<parameter key="security.encoder.pbkdf2.class">Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder</parameter>
<parameter key="security.encoder.bcrypt.class">Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder</parameter>
<parameter key="security.encoder.argon2i.class">Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder</parameter>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't use parameters for class names anymore

if (PHP_VERSION_ID < 70200 && !extension_loaded('libsodium')) {
$this->markTestSkipped('Argon2i algorithm not available.');

return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useless. markTestSkipped is throwing an exception to interrupt the execution of the test


if (PHP_VERSION_ID >= 70200) {
return $this->encodePasswordNative($raw);
} elseif (extension_loaded('libsodium')) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just if

@stof
Copy link
Member

stof commented Feb 14, 2017

Please rebase this on top of master and change the target branch of the PR

@zanbaldwin zanbaldwin changed the base branch from 2.8 to master February 14, 2017 11:56
@zanbaldwin
Copy link
Member Author

All done, thanks. I'll update the MAX_PASSWORD_LENGTH once I can find some concrete evidence that Argon2i does not have the same length limitations as BCrypt.

@stof
Copy link
Member

stof commented Feb 14, 2017

@zanderbaldwin if the algo does not explicitly says it truncates the input, it means it does not do it. you would have to look at the algo description.

@stof
Copy link
Member

stof commented Feb 14, 2017

another solution to detect it experimentally: hash a very long password, and then verify it with a string changing a single char in the string. and keep doing it, by increasing the altered index little by little. Once you find a case where the altered string is accepted in verify, it means you passed over the accepted input length.
Of course, you don't need to increase 1 by 1 (unless you have days to spend on your machine for that, or a botnet to distribute the work). You can start with a high value and increase by big numbers, until you find an accepted mismatch, and then bisect the length being the max.

@nicolas-grekas nicolas-grekas added this to the 3.x milestone Feb 14, 2017
@zanbaldwin
Copy link
Member Author

Confirmed, I tested up to 8192 characters and there are no false-positives from truncating. More than enough for a MAX_PASSWORD_LENGTH of 4096.

@jedisct1
Copy link

jedisct1 commented Feb 14, 2017

You can safely use passwords up to 2^32-1 bits long.

throw new BadCredentialsException('Invalid password.');
}

if (PHP_VERSION_ID >= 70200) {
Copy link
Member

@derrabus derrabus Feb 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer checking for features over versions whenever possible. In this case:

if (function_exists('sodium_crypto_pwhash_str')) {

What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, feature checking is more reliable when you think of php on .net or hhvm.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. I'll add that in today.
I'm also trying to find a definitive answer on whether PASSWORD_ARGON2I will be implemented for password_hash() which would be a better solution than sodium_crypto_passwd_str(), so that feature test could change.

@@ -94,11 +95,16 @@ protected function execute(InputInterface $input, OutputInterface $output)

$encoder = $this->getContainer()->get('security.encoder_factory')->getEncoder($userClass);
$bcryptWithoutEmptySalt = !$emptySalt && $encoder instanceof BCryptPasswordEncoder;
$argon2iWithoutEmptySalt = !$emptySalt && $encoder instanceof Argon2iPasswordEncoder;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest adding a a marker interface on encoders not needing the salt externally, to avoid having to know them one by one (I have the same need for detecting them in FOSUserBundle 2 to avoid generating useless salts).
what do you think @symfony/deciders ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 even though I'm not a decider; I wanted to do something like that but thought it would be adding too much for the scope of this PR.

Is that something I could implement here or separate PR?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's do it separately indeed. But the introduction of this encoder makes it worth adding such feature.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea!

@zanbaldwin
Copy link
Member Author

Hi, all requested changes have been made and I've finally got round to fixing the conflicts with the latest version of master 👍

There will be conflicts with #21620 that I'll have to fix (most likely in this PR if #21620 gets merged first since it's been marked for 3.3).


public function setUp()
{
if (PHP_VERSION_ID < 70200 && !extension_loaded('libsodium')) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it possible to run the tests if libsodium is missing, but the sodium_crypto_pwhash_str(), sodium_memzero(), and sodium_crypto_pwhash_str_verify() functions do exist?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, absolutely. I did change from version-checking to feature-checking in Argon2iPasswordEncoder but I unfortunately either forgot to update it here, or it snuck in whilst rebasing (either way I missed it and shall update when I get home from work).

The isPasswordValid() method checks for the function that it uses, but neither encodePassword() or isPasswordValid() check for the existence of sodium_memzero() - is this something that should be done in case one of the check functions has been defined in user-land code? It's a pretty far off edge-case but should at least be aknowledged...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One solution could be to check the result of ReflectionFunctionAbstract::isUserDefined, though would this be adding too much overhead?

@nicolas-grekas
Copy link
Member

src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php should also be updated to account for argon2

@zanbaldwin
Copy link
Member Author

zanbaldwin commented Mar 23, 2017

I've updated EncoderFactory but I admit that I didn't quite comprehend the EncoderFactoryTest to modify it appropriately; if this needs updating can someone point me in the right direction please?

@sstok
Copy link
Contributor

sstok commented Jun 18, 2017

EncoderFactoryTest doesn't need to be updated, both bcrypt and pbkdf2 are not listed in this test-class also.

/**
* @author Elnur Abdurrakhimov <[email protected]>
*/
class Argon2iPasswordEncoderTest extends \PHPUnit_Framework_TestCase
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to use PHPUnit\Framework\TestCase.

@chalasr
Copy link
Member

chalasr commented Sep 9, 2017

Whilst the libsodium RFC has been approved and the public API confirmed, there has been no confirmation of Argon2i becoming an official algorithm for passhword_hash(). If that is confirmed, then the implementation should absolutely use the native password_* functions since the sodium_* functions do not have an equivalent to the password_needs_rehash() function.

Confirmed it is: PHP 7.2 introduces PASSWORD_ARGON2I as algo for password_hash() (see https://wiki.php.net/rfc/argon2_password_hash).

@zanderbaldwin What's the status of this PR? Could you finish it? Or do you want someone to take it over? I would like to get this in.

@zanbaldwin
Copy link
Member Author

zanbaldwin commented Sep 12, 2017

Sure, I'll get on this in the next few days.

Does anyone know a good way to determine if the constant PASSWORD_ARGON2I is user or core defined? I'd rather feature test than test PHP_VERSION_ID >= 70200.

I'm currently testing with the php:7.2.0RC1-alpine Docker image and the constant isn't defined yet (getting really weird behaviour on 3v4l.org too).

@chalasr
Copy link
Member

chalasr commented Sep 14, 2017

@zanderbaldwin Just tried out, using defined('PASSWORD_ARGON2I') should do the job.
See https://3v4l.org/N8RCV

@nicolas-grekas
Copy link
Member

@zanbaldwin would you have time to finish this PR for the end of the week?

@zanbaldwin
Copy link
Member Author

I've made most of the changes. Some points I want to clarify are:

  • PASSWORD_ARGON2I could be user-defined in versions of PHP less than 7.2 so I also do version-testing. But I hate version checking instead of feature checking, so does anyone know of a better way of doing this?
  • There are three different ways to test for the presence of Sodium, do we need them all?
    • PHP_VERSION_ID >= 70200 && defined('PASSWORD_ARGON2I') uses the built in password_hash() functions.
    • function_exists('sodium_crypto_pwhash_str') uses the Sodium functions that are native in 7.2.
    • extension_loaded('libsodium') uses the Sodium extension for PHP versions less than 7.2.

I'm thinking of getting rid of the function_exists('sodium_crypto_pwhash_str') check, but while testing on php:7.2.0RC2-alpine Docker image I've found that the constant PASSWORD_ARGON2I is not defined (but it works completely fine on 3v4l.org). Thoughts?

return $this->encodePasswordSodiumExtension($raw);
}

throw new \LogicException('Argon2i algorithm is not supported. Please install libsodium extension or upgrade to PHP 7.2+.');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we cannot already (also) do these checks inside SecurityExtension when the config is parsed? This would exclude WTF moments at runtime.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the extension, it should be an InvalidConfigurationException

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I've made the changes in the Security component, I'll get onto putting extra checks in the SecurityBundle config parser 🙂

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, "install the libsodium extension" sounds better.

throw new \LogicException('Argon2i algorithm is not supported. Please install libsodium extension or upgrade to PHP 7.2+.');
}

private function encodePasswordNative($raw)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private methods should come after public ones

{
if (\PHP_VERSION_ID >= 70200 && defined('PASSWORD_ARGON2I')) {
$valid = !$this->isPasswordTooLong($raw) && password_verify($raw, $encoded);
} elseif (function_exists('sodium_crypto_pwhash_str_verify')) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would use early returns instead of elseif

@zanbaldwin
Copy link
Member Author

I've made some changes, but committed them in a separate commit (which can be squashed later).

  • Update all exception messages regarding the Argon2i algorithm not being supported to be the same string.
  • Add a static helper method on the Argon2iPasswordEncoder class, all checks throughout the Security component and SecurityBundle use the same method. If this changes, it only needs to be updated in one place.
  • Added a check in the SecurityExtension. Is this the correct place to put it, or should the check be directly in the MainConfiguration class using config validation callbacks?

Feedback welcomed, as always 👍

@xabbuh
Copy link
Member

xabbuh commented Sep 28, 2017

tests are currently failing (see the CI builds) :)

Copy link
Member

@fabpot fabpot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with minor text comments

@@ -14,6 +14,7 @@ CHANGELOG
* deprecated HTTP digest authentication
* deprecated command `acl:set` along with `SetAclCommand` class
* deprecated command `init:acl` along with `InitAclCommand` class
* Added support for the new Argon2i password encoder.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: dot at the end should be removed

// Argon2i encoder
if ('argon2i' === $config['algorithm']) {
if (!Argon2iPasswordEncoder::isSupported()) {
throw new InvalidConfigurationException('Argon2i algorithm is not supported. Please install libsodium extension or upgrade to PHP 7.2+.');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

install the libsodium extension?

@@ -13,6 +13,7 @@ CHANGELOG
the user will always be logged out when the user has changed between
requests.
* deprecated HTTP digest authentication
* Added a new password encoder for the Argon2i hashing algorithm.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same, no dot

use Symfony\Component\Security\Core\Exception\BadCredentialsException;

/**
* Argon2iPasswordEncoder uses the Argon2i hashing algorithm provided by libsodium.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove the "provided by libsodium" part, which is an implementation detail.

return $valid;
}

throw new \LogicException('Argon2i algorithm is not supported. Please install libsodium extension or upgrade to PHP 7.2+.');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, "install the libsodium extension" sounds better.

return $this->encodePasswordSodiumExtension($raw);
}

throw new \LogicException('Argon2i algorithm is not supported. Please install libsodium extension or upgrade to PHP 7.2+.');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, "install the libsodium extension" sounds better.

@zanbaldwin
Copy link
Member Author

That's because Travis installs the symfony/security package as version dev-master 45020b8 rather than using the Security component code of this PR.

@xabbuh
Copy link
Member

xabbuh commented Sep 28, 2017

@zanbaldwin In fact, the code is right (at least on the PHP 5.5 build, see https://travis-ci.org/symfony/symfony/jobs/280855177#L3622), but the Argon2i algorithm is not available. Tests should reflect this possibility.

{
return function_exists('sodium_crypto_pwhash_str')
|| extension_loaded('libsodium')
|| (\PHP_VERSION_ID >= 70200 && defined('PASSWORD_ARGON2I'));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defined must be fully-qualified, so that it can be resolved at compile time when the constant is defined in PHP itself (which is the case when it is set on PHP 7.2+).

and the PHP 7.2 case should be there first, so that OPcache can optimize the code. return true || function_exists('sodium_crypto_pwhash_str') || extension_loaded('libsodium') can be optimized into return true. But return function_exists('sodium_crypto_pwhash_str') || extension_loaded('libsodium') || true cannot (as earlier branches of the boolean condition must be evaluated first in PHP)

throw new BadCredentialsException('Invalid password.');
}

if (\PHP_VERSION_ID >= 70200 && defined('PASSWORD_ARGON2I')) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defined should be fully qualified here too for optimizations

*/
public function isPasswordValid($encoded, $raw, $salt)
{
if (\PHP_VERSION_ID >= 70200 && defined('PASSWORD_ARGON2I')) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defined should be fully qualified here too for optimizations

Copy link
Member

@chalasr chalasr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

almost good :)

{
const PASSWORD = 'password';

public function setUp()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be protected

'JMS\FooBundle\Entity\User7' => array(
'class' => 'Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder',
'arguments' => array(),
),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what breaks tests, as it runs on PHP 5.6. I suggest writing a dedicated test in SecurityExtensionTest, skipped if argon2i not supported.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not quite sure how to implement that - will need more details or be pointed in the right direction if I'm to implement that by end of play tomorrow, sorry!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did it for you in 51be4e5 :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, thank you very much @chalasr! Looking at that commit, it would have taken me a long time to figure that out!

if (\PHP_VERSION_ID >= 70200 && \defined('PASSWORD_ARGON2I')) {
return !$this->isPasswordTooLong($raw) && password_verify($raw, $encoded);
}
if (function_exists('sodium_crypto_pwhash_str_verify')) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be fully qualified too, considering PHP-CS-Fixer/PHP-CS-Fixer#3048 (comment) ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed, it should

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, will fully-qualify those two functions too :)


return $valid;
}
if (extension_loaded('libsodium')) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

Copy link
Member

@chalasr chalasr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All good

@zanbaldwin
Copy link
Member Author

Should I squash my five commits?

@chalasr
Copy link
Member

chalasr commented Sep 29, 2017

@zanbaldwin We can do it while merging, as you wish.
@stof requested changes have been made, is this ok for you?

@nicolas-grekas
Copy link
Member

In fact yes, squashing is needed because we can't make it when several authors are in the history.

Add the Argon2i hashing algorithm provided by libsodium as a core encoder in the Security component, and enable it in the SecurityBundle.
Credit to @chalasr for help with unit tests.
@fabpot
Copy link
Member

fabpot commented Sep 29, 2017

Thank you @zanbaldwin.

@fabpot fabpot merged commit be093dd into symfony:3.4 Sep 29, 2017
fabpot added a commit that referenced this pull request Sep 29, 2017
This PR was merged into the 3.4 branch.

Discussion
----------

[Security] Argon2i Password Encoder

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        | WIP

Since the [libsodium RFC](https://wiki.php.net/rfc/libsodium) passed with flying colours, I'd like to kick start a discussion about adding Argon2i as a password encoder to the security component. The initial code proposal in this PR supports both the upcoming public API confirmed for PHP 7.2, and the [libsodium PECL extension](https://pecl.php.net/package/libsodium) for those below 7.2 (available for PHP 5.4+).

#### Concerns

- Should the test cover hash length? At the moment the result of Argon2i is 96 characters, but because the hashing parameters are included in the result (`$argon2i$v=19$m=32768,t=4,p=1$...`) this is not guaranteed.
- I've used one password encoder class because the result *should* be the same whether running natively in 7.2 or from the PECL extension, but should the logic be split out into separate private methods (like `Argon2iPasswordEncoder::encodePassword()`) or not (like in `Argon2iPasswordEncoder::isPasswordValid()`)? Since I can't really find anything concrete on Symfony choosing one way over another I'm assuming it's down to personal preference?

#### The Future

Whilst the libsodium RFC has been approved and the public API confirmed, there has been no confirmation of Argon2i becoming an official algorithm for `passhword_hash()`. If that is confirmed, then the implementation should *absolutely* use the native `password_*` functions since the `sodium_*` functions do not have an equivalent to the `password_needs_rehash()` function.

Any feedback would be greatly appreciated 😃

Commits
-------

be093dd Argon2i Password Encoder
@zanbaldwin zanbaldwin deleted the 2.8-argon2i-encoder branch September 29, 2017 14:07
zanbaldwin added a commit to zanbaldwin/symfony-docs that referenced this pull request Oct 4, 2017
Add/modify sections for the Argon2i password encoder (symfony/symfony#21604).
This was referenced Oct 18, 2017
zanbaldwin added a commit to zanbaldwin/symfony-docs that referenced this pull request Jan 5, 2018
Add/modify sections for the Argon2i password encoder (symfony/symfony#21604).
javiereguiluz added a commit to symfony/symfony-docs that referenced this pull request Jan 10, 2018
This PR was merged into the 3.4 branch.

Discussion
----------

Argon2i Password Encoder

| Q | A |
| --- | --- |
| Doc fix? | no |
| New docs? | yes (symfony/symfony#21604) |
| Applies to | `3.4` |
| Fixed tickets | N/A |

Add sections for the Argon2i password encoder.

Commits
-------

d96fda4 Removed a duplicated reference
be4f85c Argon2i Password Encoder
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.