diff --git a/_build/redirection_map b/_build/redirection_map index 68095fcf2b8..1069505a81a 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -514,3 +514,4 @@ /service_container/3.3-di-changes https://symfony.com/doc/3.4/service_container/3.3-di-changes.html /frontend/encore/shared-entry /frontend/encore/split-chunks /testing/functional_tests_assertions /testing#testing-application-assertions +/security/named_encoders /security/named_hashers diff --git a/components/security/authentication.rst b/components/security/authentication.rst index 18b780b6d84..9cca9f18d9f 100644 --- a/components/security/authentication.rst +++ b/components/security/authentication.rst @@ -121,12 +121,12 @@ the given password is valid. This functionality is offered by the :class:`Symfony\\Component\\Security\\Core\\Authentication\\Provider\\DaoAuthenticationProvider`. It fetches the user's data from a :class:`Symfony\\Component\\Security\\Core\\User\\UserProviderInterface`, -uses a :class:`Symfony\\Component\\Security\\Core\\Encoder\\PasswordEncoderInterface` +uses a :class:`Symfony\\Component\\PasswordHasher\\Hasher\\UserPasswordHasherInterface` to create a hash of the password and returns an authenticated token if the password was valid:: + use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface; use Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider; - use Symfony\Component\Security\Core\Encoder\EncoderFactory; use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\User\UserChecker; @@ -145,14 +145,14 @@ password was valid:: // for some extra checks: is account enabled, locked, expired, etc. $userChecker = new UserChecker(); - // an array of password encoders (see below) - $encoderFactory = new EncoderFactory(...); + // an array of password hashers (see below) + $hasherFactory = new PasswordHasherFactoryInterface(...); $daoProvider = new DaoAuthenticationProvider( $userProvider, $userChecker, 'secured_area', - $encoderFactory + $hasherFactory ); $daoProvider->authenticate($unauthenticatedToken); @@ -165,69 +165,76 @@ password was valid:: It is also possible to let multiple user providers try to find the user's data, using the :class:`Symfony\\Component\\Security\\Core\\User\\ChainUserProvider`. -The Password Encoder Factory -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. _the-password-encoder-factory: + +The Password Hasher Factory +~~~~~~~~~~~~~~~~~~~~~~~~~~~ The :class:`Symfony\\Component\\Security\\Core\\Authentication\\Provider\\DaoAuthenticationProvider` -uses an encoder factory to create a password encoder for a given type of -user. This allows you to use different encoding strategies for different -types of users. The default :class:`Symfony\\Component\\Security\\Core\\Encoder\\EncoderFactory` -receives an array of encoders:: +uses a factory to create a password hasher for a given type of user. This allows +you to use different hashing strategies for different types of users. +The default :class:`Symfony\\Component\\PasswordHasher\\Hasher\\PasswordHasherFactory` +receives an array of hashers:: use Acme\Entity\LegacyUser; - use Symfony\Component\Security\Core\Encoder\EncoderFactory; - use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder; + use Symfony\Component\PasswordHasher\Hasher\MessageDigestPasswordHasher; + use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactory; use Symfony\Component\Security\Core\User\InMemoryUser; - $defaultEncoder = new MessageDigestPasswordEncoder('sha512', true, 5000); - $weakEncoder = new MessageDigestPasswordEncoder('md5', true, 1); + $defaultHasher = new MessageDigestPasswordHasher('sha512', true, 5000); + $weakHasher = new MessageDigestPasswordHasher('md5', true, 1); - $encoders = [ - InMemoryUser::class => $defaultEncoder, - LegacyUser::class => $weakEncoder, + $hashers = [ + InMemoryUser::class => $defaultHasher, + LegacyUser::class => $weakHasher, // ... ]; - $encoderFactory = new EncoderFactory($encoders); + $hasherFactory = new PasswordHasherFactory($hashers); -Each encoder should implement :class:`Symfony\\Component\\Security\\Core\\Encoder\\PasswordEncoderInterface` +Each hasher should implement :class:`Symfony\\Component\\PasswordHasher\\Hasher\\UserPasswordHasherInterface` or be an array with a ``class`` and an ``arguments`` key, which allows the -encoder factory to construct the encoder only when it is needed. +hasher factory to construct the hasher only when it is needed. + +.. _creating-a-custom-password-encoder: -Creating a custom Password Encoder -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Creating a custom Password Hasher +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -There are many built-in password encoders. But if you need to create your +There are many built-in password hasher. But if you need to create your own, it needs to follow these rules: -#. The class must implement :class:`Symfony\\Component\\Security\\Core\\Encoder\\PasswordEncoderInterface` - (you can also extend :class:`Symfony\\Component\\Security\\Core\\Encoder\\BasePasswordEncoder`); +#. The class must implement :class:`Symfony\\Component\\PasswordHasher\\Hasher\\UserPasswordHasherInterface` + (you can also extend :class:`Symfony\\Component\\PasswordHasher\\Hasher\\UserPasswordHasher`); #. The implementations of - :method:`Symfony\\Component\\Security\\Core\\Encoder\\PasswordEncoderInterface::encodePassword` + :method:`Symfony\\Component\\PasswordHasher\\Hasher\\UserPasswordHasherInterface::hashPassword` and - :method:`Symfony\\Component\\Security\\Core\\Encoder\\PasswordEncoderInterface::isPasswordValid` + :method:`Symfony\\Component\\PasswordHasher\\Hasher\\UserPasswordHasherInterface::isPasswordValid` must first of all make sure the password is not too long, i.e. the password length is no longer than 4096 characters. This is for security reasons (see `CVE-2013-5750`_), and you can use the - :method:`Symfony\\Component\\Security\\Core\\Encoder\\BasePasswordEncoder::isPasswordTooLong` + :method:`Symfony\\Component\\PasswordHasher\\Hasher\\CheckPasswordLengthTrait::isPasswordTooLong` method for this check:: - use Symfony\Component\Security\Core\Encoder\BasePasswordEncoder; + use Symfony\Component\PasswordHasher\Hasher\CheckPasswordLengthTrait; + use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasher; use Symfony\Component\Security\Core\Exception\BadCredentialsException; - class FoobarEncoder extends BasePasswordEncoder + class FoobarHasher extends UserPasswordHasher { - public function encodePassword($raw, $salt) + use CheckPasswordLengthTrait; + + public function hashPassword(UserInterface $user, string $plainPassword): string { - if ($this->isPasswordTooLong($raw)) { + if ($this->isPasswordTooLong($user->getPassword())) { throw new BadCredentialsException('Invalid password.'); } // ... } - public function isPasswordValid($encoded, $raw, $salt) + public function isPasswordValid(UserInterface $user, string $plainPassword) { - if ($this->isPasswordTooLong($raw)) { + if ($this->isPasswordTooLong($user->getPassword())) { return false; } @@ -235,13 +242,15 @@ own, it needs to follow these rules: } } -Using Password Encoders -~~~~~~~~~~~~~~~~~~~~~~~ +.. _using-password-encoders: -When the :method:`Symfony\\Component\\Security\\Core\\Encoder\\EncoderFactory::getEncoder` -method of the password encoder factory is called with the user object as -its first argument, it will return an encoder of type :class:`Symfony\\Component\\Security\\Core\\Encoder\\PasswordEncoderInterface` -which should be used to encode this user's password:: +Using Password Hashers +~~~~~~~~~~~~~~~~~~~~~~ + +When the :method:`Symfony\\Component\\PasswordHasher\\Hasher\\PasswordHasherFactory::getPasswordHasher` +method of the password hasher factory is called with the user object as +its first argument, it will return a hasher of type :class:`Symfony\\Component\\PasswordHasher\\PasswordHasherInterface` +which should be used to hash this user's password:: // a Acme\Entity\LegacyUser instance $user = ...; @@ -249,12 +258,12 @@ which should be used to encode this user's password:: // the password that was submitted, e.g. when registering $plainPassword = ...; - $encoder = $encoderFactory->getEncoder($user); + $hasher = $hasherFactory->getPasswordHasher($user); - // returns $weakEncoder (see above) - $encodedPassword = $encoder->encodePassword($plainPassword, $user->getSalt()); + // returns $weakHasher (see above) + $hashedPassword = $hasher->hashPassword($user, $plainPassword); - $user->setPassword($encodedPassword); + $user->setPassword($hashedPassword); // ... save the user @@ -267,11 +276,7 @@ in) is correct, you can use:: // the submitted password, e.g. from the login form $plainPassword = ...; - $validPassword = $encoder->isPasswordValid( - $user->getPassword(), // the encoded password - $plainPassword, // the submitted password - $user->getSalt() - ); + $validPassword = $hasher->isPasswordValid($user, $plainPassword); Authentication Events --------------------- diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index 630c65879f1..a7b644add9a 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -40,7 +40,7 @@ Some of these options define tens of sub-options and they are explained in separate articles: * `access_control`_ -* `encoders`_ +* `hashers`_ * `firewalls`_ * `providers`_ * `role_hierarchy`_ @@ -120,15 +120,16 @@ and to allow anonymous users to the login form page. This option is explained in detail in :doc:`/security/access_control`. -encoders --------- +.. _encoders: -This option defines the algorithm used to *encode* the password of the users. -Although Symfony calls it *"password encoding"* for historical reasons, this is -in fact, *"password hashing"*. +hashers +------- + +This option defines the algorithm used to *hash* the password of the users +(which in previous Symfony versions was wrongly called *"password encoding"*). If your app defines more than one user class, each of them can define its own -encoding algorithm. Also, each algorithm defines different config options: +hashing algorithm. Also, each algorithm defines different config options: .. configuration-block:: @@ -138,25 +139,25 @@ encoding algorithm. Also, each algorithm defines different config options: security: # ... - encoders: - # auto encoder with default options + password_hashers: + # auto hasher with default options App\Entity\User: 'auto' - # auto encoder with custom options + # auto hasher with custom options App\Entity\User: algorithm: 'auto' cost: 15 - # Sodium encoder with default options + # Sodium hasher with default options App\Entity\User: 'sodium' - # Sodium encoder with custom options + # Sodium hasher with custom options App\Entity\User: algorithm: 'sodium' memory_cost: 16384 # Amount in KiB. (16384 = 16 MiB) time_cost: 2 # Number of iterations - # MessageDigestPasswordEncoder encoder using SHA512 hashing with default options + # MessageDigestPasswordHasher hasher using SHA512 hashing with default options App\Entity\User: 'sha512' .. code-block:: xml @@ -173,37 +174,37 @@ encoding algorithm. Also, each algorithm defines different config options: - - + - - + - - + - + - - - + @@ -217,55 +218,61 @@ encoding algorithm. Also, each algorithm defines different config options: $container->loadFromExtension('security', [ // ... - 'encoders' => [ - // auto encoder with default options + 'password_hashers' => [ + // auto hasher with default options User::class => [ 'algorithm' => 'auto', ], - // auto encoder with custom options + // auto hasher with custom options User::class => [ 'algorithm' => 'auto', 'cost' => 15, ], - // Sodium encoder with default options + // Sodium hasher with default options User::class => [ 'algorithm' => 'sodium', ], - // Sodium encoder with custom options + // Sodium hasher with custom options User::class => [ 'algorithm' => 'sodium', 'memory_cost' => 16384, // Amount in KiB. (16384 = 16 MiB) 'time_cost' => 2, // Number of iterations ], - // MessageDigestPasswordEncoder encoder using SHA512 hashing with default options + // MessageDigestPasswordHasher hasher using SHA512 hashing with default options User::class => [ 'algorithm' => 'sha512', ], ], ]); +.. versionadded:: 5.3 + + The ``password_hashers`` option was introduced in Symfony 5.3. In previous + versions it was called ``encoders``. + .. tip:: - You can also create your own password encoders as services and you can even - select a different password encoder for each user instance. Read - :doc:`this article ` for more details. + You can also create your own password hashers as services and you can even + select a different password hasher for each user instance. Read + :doc:`this article ` for more details. .. tip:: - Encoding passwords is resource intensive and takes time in order to generate + Hashing passwords is resource intensive and takes time in order to generate secure password hashes. In tests however, secure hashes are not important, so - you can change the encoders configuration in ``test`` environment to run tests faster: + you can change the password hasher configuration in ``test`` environment to + run tests faster: .. configuration-block:: .. code-block:: yaml # config/packages/test/security.yaml - encoders: + password_hashers: # Use your user class name here App\Entity\User: algorithm: auto # This should be the same value as in config/packages/security.yaml @@ -289,7 +296,7 @@ encoding algorithm. Also, each algorithm defines different config options: - loadFromExtension('security', [ - 'encoders' => [ + 'password_hashers' => [ // Use your user class name here User::class => [ 'algorithm' => 'auto', // This should be the same value as in config/packages/security.yaml @@ -318,44 +325,46 @@ encoding algorithm. Also, each algorithm defines different config options: .. _reference-security-sodium: .. _using-the-argon2i-password-encoder: +.. _using-the-sodium-password-encoder: -Using the Sodium Password Encoder -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Using the Sodium Password Hasher +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -It uses the `Argon2 key derivation function`_ and it's the encoder recommended +It uses the `Argon2 key derivation function`_ and it's the hasher recommended by Symfony. Argon2 support was introduced in PHP 7.2, but if you use an earlier PHP version, you can install the `libsodium`_ PHP extension. -The encoded passwords are ``96`` characters long, but due to the hashing +The hashed passwords are ``96`` characters long, but due to the hashing requirements saved in the resulting hash this may change in the future, so make sure to allocate enough space for them to be persisted. Also, passwords include the `cryptographic salt`_ inside them (it's generated automatically for each new password) so you don't have to deal with it. .. _reference-security-encoder-auto: +.. _using-the-auto-password-encoder: -Using the "auto" Password Encoder -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Using the "auto" Password Hasher +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -It selects automatically the best possible encoder. Currently, it tries to use +It selects automatically the best possible hasher. Currently, it tries to use Sodium by default and falls back to the `bcrypt password hashing function`_ if not possible. In the future, when PHP adds new hashing techniques, it may use different password hashers. -It produces encoded passwords with ``60`` characters long, so make sure to +It produces hashed passwords with ``60`` characters long, so make sure to allocate enough space for them to be persisted. Also, passwords include the `cryptographic salt`_ inside them (it's generated automatically for each new password) so you don't have to deal with it. Its only configuration option is ``cost``, which is an integer in the range of ``4-31`` (by default, ``13``). Each single increment of the cost **doubles the -time** it takes to encode a password. It's designed this way so the password +time** it takes to hash a password. It's designed this way so the password strength can be adapted to the future improvements in computation power. You can change the cost at any time — even if you already have some passwords -encoded using a different cost. New passwords will be encoded using the new -cost, while the already encoded ones will be validated using a cost that was -used back when they were encoded. +hashed using a different cost. New passwords will be hashed using the new +cost, while the already hashed ones will be validated using a cost that was +used back when they were hashed. .. tip:: @@ -364,13 +373,14 @@ used back when they were encoded. environment configuration. .. _reference-security-pbkdf2: +.. _using-the-pbkdf2-encoder: -Using the PBKDF2 Encoder -~~~~~~~~~~~~~~~~~~~~~~~~ +Using the PBKDF2 Hasher +~~~~~~~~~~~~~~~~~~~~~~~ -Using the `PBKDF2`_ encoder is no longer recommended since PHP added support for +Using the `PBKDF2`_ hasher is no longer recommended since PHP added support for Sodium and BCrypt. Legacy application still using it are encouraged to upgrade -to those newer encoding algorithms. +to those newer hashing algorithms. firewalls --------- diff --git a/security.rst b/security.rst index 57c306fb26d..f5f282f7efa 100644 --- a/security.rst +++ b/security.rst @@ -200,12 +200,13 @@ here: :doc:`User Providers `. .. _security-encoding-user-password: .. _encoding-the-user-s-password: +.. _2c-encoding-passwords: -2c) Encoding Passwords ----------------------- +2c) Hashing Passwords +--------------------- Not all applications have "users" that need passwords. *If* your users have passwords, -you can control how those passwords are encoded in ``security.yaml``. The ``make:user`` +you can control how those passwords are hashed in ``security.yaml``. The ``make:user`` command will pre-configure this for you: .. configuration-block:: @@ -216,10 +217,10 @@ command will pre-configure this for you: security: # ... - encoders: + password_hashers: # use your user class name here App\Entity\User: - # Use native password encoder, which auto-selects the best + # Use native password hasher, which auto-selects the best # possible hashing algorithm (starting from Symfony 5.3 this is "bcrypt") algorithm: auto @@ -238,7 +239,7 @@ command will pre-configure this for you: - @@ -254,7 +255,7 @@ command will pre-configure this for you: $container->loadFromExtension('security', [ // ... - 'encoders' => [ + 'password_hashers' => [ User::class => [ 'algorithm' => 'auto', 'cost' => 12, @@ -264,8 +265,13 @@ command will pre-configure this for you: // ... ]); -Now that Symfony knows *how* you want to encode the passwords, you can use the -``UserPasswordEncoderInterface`` service to do this before saving your users to +.. versionadded:: 5.3 + + The ``password_hashers`` option was introduced in Symfony 5.3. In previous + versions it was called ``encoders``. + +Now that Symfony knows *how* you want to hash the passwords, you can use the +``UserPasswordHasherInterface`` service to do this before saving your users to the database. .. _user-data-fixture: @@ -280,22 +286,22 @@ create dummy database users: The class name of the fixtures to create (e.g. AppFixtures): > UserFixtures -Use this service to encode the passwords: +Use this service to hash the passwords: .. code-block:: diff // src/DataFixtures/UserFixtures.php - + use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; + + use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; // ... class UserFixtures extends Fixture { - + private $passwordEncoder; + + private $passwordHasher; - + public function __construct(UserPasswordEncoderInterface $passwordEncoder) + + public function __construct(UserPasswordHasherInterface $passwordHasher) + { - + $this->passwordEncoder = $passwordEncoder; + + $this->passwordHasher = $passwordHasher; + } public function load(ObjectManager $manager) @@ -303,7 +309,7 @@ Use this service to encode the passwords: $user = new User(); // ... - + $user->setPassword($this->passwordEncoder->encodePassword( + + $user->setPassword($this->passwordHasher->hashPassword( + $user, + 'the_new_password' + )); @@ -312,11 +318,11 @@ Use this service to encode the passwords: } } -You can manually encode a password by running: +You can manually hash a password by running: .. code-block:: terminal - $ php bin/console security:encode-password + $ php bin/console security:hash-password .. _security-yaml-firewalls: .. _security-firewalls: @@ -1503,7 +1509,7 @@ Authentication (Identifying/Logging in the User) security/remember_me security/impersonating_user security/user_checkers - security/named_encoders + security/named_hashers security/multiple_guard_authenticators security/firewall_restriction security/csrf diff --git a/security/named_encoders.rst b/security/named_hashers.rst similarity index 72% rename from security/named_encoders.rst rename to security/named_hashers.rst index 8ff58f0fbef..c377cfa28e9 100644 --- a/security/named_encoders.rst +++ b/security/named_hashers.rst @@ -1,10 +1,10 @@ .. index:: single: Security; Named Encoders -How to Use A Different Password Encoder Algorithm Per User -========================================================== +How to Use A Different Password Hasher Algorithm Per User +========================================================= -Usually, the same password encoder is used for all users by configuring it +Usually, the same password hasher is used for all users by configuring it to apply to all instances of a specific class: .. configuration-block:: @@ -14,7 +14,7 @@ to apply to all instances of a specific class: # config/packages/security.yaml security: # ... - encoders: + password_hashers: App\Entity\User: algorithm: auto cost: 12 @@ -33,7 +33,7 @@ to apply to all instances of a specific class: > - @@ -47,7 +47,7 @@ to apply to all instances of a specific class: $container->loadFromExtension('security', [ // ... - 'encoders' => [ + 'password_hashers' => [ User::class => [ 'algorithm' => 'auto', 'cost' => 12, @@ -55,13 +55,13 @@ to apply to all instances of a specific class: ], ]); -Another option is to use a "named" encoder and then select which encoder +Another option is to use a "named" hasher and then select which hasher you want to use dynamically. In the previous example, you've set the ``auto`` algorithm for ``App\Entity\User``. This may be secure enough for a regular user, but what if you want your admins to have a stronger algorithm, for example ``auto`` with a higher cost. This can -be done with named encoders: +be done with named hashers: .. configuration-block:: @@ -70,7 +70,7 @@ be done with named encoders: # config/packages/security.yaml security: # ... - encoders: + password_hashers: harsh: algorithm: auto cost: 15 @@ -90,7 +90,7 @@ be done with named encoders: - @@ -101,7 +101,7 @@ be done with named encoders: // config/packages/security.php $container->loadFromExtension('security', [ // ... - 'encoders' => [ + 'password_hashers' => [ 'harsh' => [ 'algorithm' => 'auto', 'cost' => '15', @@ -115,33 +115,33 @@ be done with named encoders: then the recommended hashing algorithm to use is :ref:`Sodium `. -This creates an encoder named ``harsh``. In order for a ``User`` instance +This creates a hasher named ``harsh``. In order for a ``User`` instance to use it, the class must implement -:class:`Symfony\\Component\\Security\\Core\\Encoder\\EncoderAwareInterface`. -The interface requires one method - ``getEncoderName()`` - which should return -the name of the encoder to use:: +:class:`Symfony\\Component\\PasswordHasher\\Hasher\\PasswordHasherAwareInterface`. +The interface requires one method - ``getPasswordHasherName()`` - which should return +the name of the hasher to use:: // src/Entity/User.php namespace App\Entity; - use Symfony\Component\Security\Core\Encoder\EncoderAwareInterface; + use Symfony\Component\PasswordHasher\Hasher\PasswordHasherAwareInterface; use Symfony\Component\Security\Core\User\UserInterface; - class User implements UserInterface, EncoderAwareInterface + class User implements UserInterface, PasswordHasherAwareInterface { - public function getEncoderName(): ?string + public function getPasswordHasherName(): ?string { if ($this->isAdmin()) { return 'harsh'; } - return null; // use the default encoder + return null; // use the default hasher } } -If you created your own password encoder implementing the -:class:`Symfony\\Component\\Security\\Core\\Encoder\\PasswordEncoderInterface`, -you must register a service for it in order to use it as a named encoder: +If you created your own password hasher implementing the +:class:`SSymfony\\Component\\PasswordHasher\\Hasher\\UserPasswordHasherInterface`, +you must register a service for it in order to use it as a named hasher: .. configuration-block:: @@ -150,9 +150,9 @@ you must register a service for it in order to use it as a named encoder: # config/packages/security.yaml security: # ... - encoders: - app_encoder: - id: 'App\Security\Encoder\MyCustomPasswordEncoder' + password_hashers: + app_hasher: + id: 'App\Security\Hasher\MyCustomPasswordHasher' .. code-block:: xml @@ -169,8 +169,8 @@ you must register a service for it in order to use it as a named encoder: - + @@ -178,18 +178,18 @@ you must register a service for it in order to use it as a named encoder: // config/packages/security.php // ... - use App\Security\Encoder\MyCustomPasswordEncoder; + use App\Security\Hasher\MyCustomPasswordHasher; $container->loadFromExtension('security', [ // ... - 'encoders' => [ - 'app_encoder' => [ - 'id' => MyCustomPasswordEncoder::class, + 'password_hashers' => [ + 'app_hasher' => [ + 'id' => MyCustomPasswordHasher::class, ], ], ]); -This creates an encoder named ``app_encoder`` from a service with the ID -``App\Security\Encoder\MyCustomPasswordEncoder``. +This creates a hasher named ``app_hasher`` from a service with the ID +``App\Security\Hasher\MyCustomPasswordHasher``. .. _`libsodium`: https://pecl.php.net/package/libsodium diff --git a/security/password_migration.rst b/security/password_migration.rst index 52e263d478c..f49e01c887a 100644 --- a/security/password_migration.rst +++ b/security/password_migration.rst @@ -9,16 +9,18 @@ hash algorithms. This means that if a better hash algorithm is supported on your system, the user's password should be *rehashed* using the newer algorithm and stored. That's possible with the ``migrate_from`` option: -#. `Configure a new Encoder Using "migrate_from"`_ +#. `Configure a new Hasher Using "migrate_from"`_ #. `Upgrade the Password`_ -#. Optionally, `Trigger Password Migration From a Custom Encoder`_ +#. Optionally, `Trigger Password Migration From a Custom Hasher`_ -Configure a new Encoder Using "migrate_from" ----------------------------------------------- +.. _configure-a-new-encoder-using migrate_from: + +Configure a new Hasher Using "migrate_from" +------------------------------------------- When a better hashing algorithm becomes available, you should keep the existing -encoder(s), rename it, and then define the new one. Set the ``migrate_from`` option -on the new encoder to point to the old, legacy encoder(s): +hasher(s), rename it, and then define the new one. Set the ``migrate_from`` option +on the new hasher to point to the old, legacy hasher(s): .. configuration-block:: @@ -28,19 +30,19 @@ on the new encoder to point to the old, legacy encoder(s): security: # ... - encoders: - # an encoder used in the past for some users + password_hashers: + # a hasher used in the past for some users legacy: algorithm: sha256 encode_as_base64: false iterations: 1 App\Entity\User: - # the new encoder, along with its options + # the new hasher, along with its options algorithm: sodium migrate_from: - - bcrypt # uses the "bcrypt" encoder with the default options - - legacy # uses the "legacy" encoder configured above + - bcrypt # uses the "bcrypt" hasher with the default options + - legacy # uses the "legacy" hasher configured above .. code-block:: xml @@ -57,22 +59,22 @@ on the new encoder to point to the old, legacy encoder(s): - - - + - + bcrypt - + legacy - + @@ -82,7 +84,7 @@ on the new encoder to point to the old, legacy encoder(s): $container->loadFromExtension('security', [ // ... - 'encoders' => [ + 'password_hashers' => [ 'legacy' => [ 'algorithm' => 'sha256', 'encode_as_base64' => false, @@ -90,11 +92,11 @@ on the new encoder to point to the old, legacy encoder(s): ], 'App\Entity\User' => [ - // the new encoder, along with its options + // the new hasher, along with its options 'algorithm' => 'sodium', 'migrate_from' => [ - 'bcrypt', // uses the "bcrypt" encoder with the default options - 'legacy', // uses the "legacy" encoder configured above + 'bcrypt', // uses the "bcrypt" hasher with the default options + 'legacy', // uses the "legacy" hasher configured above ], ], ], @@ -102,14 +104,14 @@ on the new encoder to point to the old, legacy encoder(s): With this setup: -* New users will be encoded with the new algorithm; +* New users will be hashed with the new algorithm; * Whenever a user logs in whose password is still stored using the old algorithm, Symfony will verify the password with the old algorithm and then rehash and update the password using the new algorithm. .. tip:: - The *auto*, *native*, *bcrypt* and *argon* encoders automatically enable + The *auto*, *native*, *bcrypt* and *argon* hashers automatically enable password migration using the following list of ``migrate_from`` algorithms: #. :ref:`PBKDF2 ` (which uses :phpfunction:`hash_pbkdf2`); @@ -117,7 +119,7 @@ With this setup: Both use the ``hash_algorithm`` setting as the algorithm. It is recommended to use ``migrate_from`` instead of ``hash_algorithm``, unless the *auto* - encoder is used. + hasher is used. Upgrade the Password -------------------- @@ -182,10 +184,10 @@ storing the newly created password hash:: { // ... - public function upgradePassword(UserInterface $user, string $newEncodedPassword): void + public function upgradePassword(UserInterface $user, string $newHashedPassword): void { - // set the new encoded password on the User object - $user->setPassword($newEncodedPassword); + // set the new hashed password on the User object + $user->setPassword($newHashedPassword); // execute the queries on the database $this->getEntityManager()->flush(); @@ -211,34 +213,36 @@ the user provider:: { // ... - public function upgradePassword(UserInterface $user, string $newEncodedPassword): void + public function upgradePassword(UserInterface $user, string $newHashedPassword): void { - // set the new encoded password on the User object - $user->setPassword($newEncodedPassword); + // set the new hashed password on the User object + $user->setPassword($newHashedPassword); // ... store the new password } } -Trigger Password Migration From a Custom Encoder ------------------------------------------------- +.. _trigger-password-migration-from-a-custom-encoder: + +Trigger Password Migration From a Custom Hasher +----------------------------------------------- -If you're using a custom password encoder, you can trigger the password +If you're using a custom password hasher, you can trigger the password migration by returning ``true`` in the ``needsRehash()`` method:: - // src/Security/CustomPasswordEncoder.php + // src/Security/CustomPasswordHasher.php namespace App\Security; // ... - use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface; + use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; - class CustomPasswordEncoder implements PasswordEncoderInterface + class CustomPasswordHasher implements UserPasswordHasherInterface { // ... - public function needsRehash(string $encoded): bool + public function needsRehash(string $hashed): bool { - // check whether the current password is hash using an outdated encoder + // check whether the current password is hashed using an outdated hasher $hashIsOutdated = ...; return $hashIsOutdated; diff --git a/security/user_provider.rst b/security/user_provider.rst index e7a3cdec79a..9c3ca7ba3ad 100644 --- a/security/user_provider.rst +++ b/security/user_provider.rst @@ -224,7 +224,7 @@ prototypes and for limited applications that don't store users in databases. This user provider stores all user information in a configuration file, including their passwords. That's why the first step is to configure how these -users will encode their passwords: +users will hash their passwords: .. configuration-block:: @@ -233,7 +233,7 @@ users will encode their passwords: # config/packages/security.yaml security: # ... - encoders: + password_hashers: # this internal class is used by Symfony to represent in-memory users # (the 'InMemoryUser' class was introduced in Symfony 5.3. # In previous versions it was called 'User') @@ -257,7 +257,7 @@ users will encode their passwords: - @@ -274,18 +274,18 @@ users will encode their passwords: $container->loadFromExtension('security', [ // ... - 'encoders' => [ + 'password_hashers' => [ User::class => [ 'algorithm' => 'auto', ], ], ]); -Then, run this command to encode the plain text passwords of your users: +Then, run this command to hash the plain text passwords of your users: .. code-block:: terminal - $ php bin/console security:encode-password + $ php bin/console security:hash-password Now you can configure all the user information in ``config/packages/security.yaml``: @@ -304,7 +304,7 @@ Now you can configure all the user information in ``config/packages/security.yam .. caution:: When using a ``memory`` provider, and not the ``auto`` algorithm, you have - to choose an encoding without salt (i.e. ``bcrypt``). + to choose a hashing algorithm without salt (i.e. ``bcrypt``). .. _security-ldap-user-provider: @@ -427,13 +427,13 @@ command will generate a nice skeleton to get you started:: } /** - * Upgrades the encoded password of a user, typically for using a better hash algorithm. + * Upgrades the hashed password of a user, typically for using a better hash algorithm. */ - public function upgradePassword(UserInterface $user, string $newEncodedPassword): void + public function upgradePassword(UserInterface $user, string $newHashedPassword): void { - // TODO: when encoded passwords are in use, this method should: + // TODO: when hashed passwords are in use, this method should: // 1. persist the new password in the user storage - // 2. update the $user object with $user->setPassword($newEncodedPassword); + // 2. update the $user object with $user->setPassword($newHashedPassword); } }