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

Skip to content

[Security] Add NativePasswordEncoder #31140

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
Apr 18, 2019

Conversation

nicolas-grekas
Copy link
Member

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

This PR adds a new NativePasswordEncoder that defaults to the best available hashing algo to password_hash(). Best is determined by "us" or "php", the goal being that this will change in the future as new algos are published.

This provides a native encoder that we should recommend using by default.

@nicolas-grekas
Copy link
Member Author

nicolas-grekas commented Apr 17, 2019

Todo:

  • fix tests
  • update SecurityExtension
  • find a default cost for Argon2 that is at least as good as 13 for Bcrypt's (any idea?)

@ro0NL
Copy link
Contributor

ro0NL commented Apr 17, 2019

is there any reason to keep BCryptPasswordEncoder?

@nicolas-grekas
Copy link
Member Author

is there any reason to keep BCryptPasswordEncoder?

I don't think so, we should deprecate it in 4.4

@nicolas-grekas nicolas-grekas force-pushed the sec-encoder-native branch 3 times, most recently from adcef7e to 40f1daf Compare April 17, 2019 14:12
Copy link
Member Author

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

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

PR ready

@@ -416,11 +416,14 @@ private function addEncodersSection(ArrayNodeDefinition $rootNode)
->integerNode('cost')
->min(4)
->max(31)
->defaultValue(13)
->defaultNull()
Copy link
Member Author

Choose a reason for hiding this comment

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

The defaults belong to the implementation now, not to the configuration.

->end()
->scalarNode('memory_cost')->defaultNull()->end()
->scalarNode('time_cost')->defaultNull()->end()
->scalarNode('threads')->defaultNull()->end()
->scalarNode('threads')
Copy link
Member Author

Choose a reason for hiding this comment

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

libsodium hardcodes threads to 1, and this makes sense in PHP too.

@@ -532,6 +533,10 @@ private function createEncoder($config, ContainerBuilder $container)
return new Reference($config['id']);
}

if ('auto' === $config['algorithm']) {
Copy link
Member Author

Choose a reason for hiding this comment

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

"auto" should be the recommended default - I'm going to submit it as a recipe

'class' => NativePasswordEncoder::class,
'arguments' => [
$config['time_cost'],
(($config['memory_cost'] ?? 0) << 10) ?: null,
Copy link
Member Author

@nicolas-grekas nicolas-grekas Apr 17, 2019

Choose a reason for hiding this comment

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

memory_cost (from password_hash) is in kib but SodiumPasswordEncoder and NativePasswordEncoder accept bytes - this makes the conversion

{
$cost = $cost ?? 13;
$opsLimit = $opsLimit ?? max(6, \defined('SODIUM_CRYPTO_PWHASH_OPSLIMIT_MODERATE') ? \SODIUM_CRYPTO_PWHASH_OPSLIMIT_MODERATE : 6);
$memLimit = $memLimit ?? max(64 * 1024 * 1024, \defined('SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE') ? \SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE : 64 * 1024 * 1024);
Copy link
Member Author

Choose a reason for hiding this comment

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

Argon2 with opslimit = 6 & memlimit=64MiB roughly operate in the same amount of time as BCrypt's cost=13
Also, in PHP7.4 SODIUM_CRYPTO_PWHASH_OPSLIMIT_MODERATE=6 and SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE=32MiB
So our defaults are a bit more strict than PHP 7.4, which matches the fact that a cost=13 for BCrypt is stricter than PHP's default.

Copy link

Choose a reason for hiding this comment

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

Just wondering about using SODIUM Constants in the Native Encoder, as what happens if PHP >7.2 but Sodium is NOT installed. Native should work?

Copy link
Member

@chalasr chalasr May 31, 2019

Choose a reason for hiding this comment

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

The code above checks that SODUM_* constants are defined before using them, and fallback to sensible defaults (equivalent hardcoded values) otherwise

throw new \InvalidArgumentException('$cost must be in the range of 4-31.');
}

$this->algo = \defined('PASSWORD_ARGON2I') ? max(PASSWORD_DEFAULT, \defined('PASSWORD_ARGON2ID') ? PASSWORD_ARGON2ID : PASSWORD_ARGON2I) : PASSWORD_DEFAULT;
Copy link
Member Author

Choose a reason for hiding this comment

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

PASSWORD_ARGON2ID > PASSWORD_ARGON2I > PASSWORD_BCRYPT, unless PASSWORD_DEFAULT is set to an even higher algo in the future

Copy link
Contributor

Choose a reason for hiding this comment

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

So if the argon algo is available the native encoder and the sodium encoder basically do the same thing?

I am wondering if it would be better for DX to use only PASSWORD_DEFAULT in the native encoder.

Then the config would become:
auto => use best supported / recommended encoder
native => use php default algo
sodium => use best supported / available sodium algo

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 think always using the best available algo is required for best security. I don't understand how that relates to DX.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think always using the best available algo is required for best security.

Agreed.

I don't understand how that relates to DX.

I just think it might be confusing that there are 2 encoders that can do the same thing (encode with the argon algo).

@@ -532,6 +533,10 @@ private function createEncoder($config, ContainerBuilder $container)
return new Reference($config['id']);
}

if ('auto' === $config['algorithm']) {
$config['algorithm'] = SodiumPasswordEncoder::isSupported() ? 'sodium' : 'native';
Copy link
Member

Choose a reason for hiding this comment

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

is the sodium encoder superior to our native encoder? Is that why it's preferred?

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, in two aspects: it is faster for the same cost parameters, and it can be more up to date than the native one, as an extension can move faster to new algos.

Copy link
Member

Choose a reason for hiding this comment

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

being faster for the same cost may not actually be superior. The whole point of a password hashing algorithm is that it should not be too fast.

Copy link
Member Author

Choose a reason for hiding this comment

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

being faster for the same cost may not actually be superior. The whole point of a password hashing algorithm is that it should not be too fast.

this answer is wrong ;)
what matters is that attackers must spend significant resources to compute password hashes when they leaked. Being faster for the same cost is just improving UX and reducing greenhouse emissions. So yes, it matters a lot.

@nicolas-grekas nicolas-grekas force-pushed the sec-encoder-native branch 2 times, most recently from 4fcca31 to 6aa50d1 Compare April 17, 2019 19:19
@chalasr
Copy link
Member

chalasr commented Apr 18, 2019

Thank you @nicolas-grekas.

@chalasr chalasr merged commit 28f7961 into symfony:master Apr 18, 2019
chalasr pushed a commit that referenced this pull request Apr 18, 2019
This PR was merged into the 4.3-dev branch.

Discussion
----------

[Security] Add NativePasswordEncoder

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

This PR adds a new `NativePasswordEncoder` that defaults to the best available hashing algo to `password_hash()`. Best is determined by "us" or "php", the goal being that this will change in the future as new algos are published.

This provides a native encoder that we should recommend using by default.

Commits
-------

28f7961 [Security] Add NativePasswordEncoder
@nicolas-grekas nicolas-grekas deleted the sec-encoder-native branch April 18, 2019 14:01
chalasr pushed a commit that referenced this pull request Apr 19, 2019
… NativePasswordEncoder (nicolas-grekas)

This PR was merged into the 4.3-dev branch.

Discussion
----------

[Security] deprecate BCryptPasswordEncoder in favor of NativePasswordEncoder

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

Follow up of #31140

Commits
-------

e197398 [Security] deprecate BCryptPasswordEncoder in favor of NativePasswordEncoder
@nicolas-grekas nicolas-grekas modified the milestones: next, 4.3 Apr 30, 2019
@fabpot fabpot mentioned this pull request May 9, 2019
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.

8 participants