-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
[Security] Added article on password migrations #12467
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
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
.. index:: | ||
single: Security; How to Migrate a Password Hash | ||
|
||
How to Migrate a Password Hash | ||
============================== | ||
|
||
.. versionadded:: 4.4 | ||
|
||
Password migration was introduced in Symfony 4.4. | ||
|
||
In order to protect passwords, it is recommended to store them using the latest | ||
hash algorithms. This means that if a better hash algorithm is supported on the | ||
system, the user's password should be rehashed and stored. Symfony provides this | ||
functionality when a user is successfully authenticated. | ||
|
||
To enable this, make sure you apply the following steps to your application: | ||
|
||
#. `Configure a new Encoder Using "migrate_from"`_ | ||
#. `Upgrade the Password`_ | ||
#. Optionally, `Trigger Password Migration From a Custom Encoder`_ | ||
|
||
Configure a new Encoder Using "migrate_from" | ||
-------------------------------------------- | ||
|
||
When configuring a new encoder, you can specify a list of legacy encoders by | ||
using the ``migrate_from`` option: | ||
|
||
.. configuration-block:: | ||
|
||
.. code-block:: yaml | ||
|
||
# config/packages/security.yaml | ||
security: | ||
# ... | ||
|
||
encoders: | ||
legacy: | ||
algorithm: sha256 | ||
encode_as_base64: false | ||
iterations: 1 | ||
|
||
App\Entity\User: | ||
# the new encoder, along with its options | ||
algorithm: sodium | ||
migrate_from: | ||
- bcrypt # uses the "bcrypt" encoder with the default options | ||
- legacy # uses the "legacy" encoder configured above | ||
|
||
.. code-block:: xml | ||
|
||
<!-- config/packages/security.xml --> | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<container xmlns="http://symfony.com/schema/dic/services" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xmlns:security="http://symfony.com/schema/dic/security" | ||
xsi:schemaLocation="http://symfony.com/schema/dic/security | ||
https://symfony.com/schema/dic/security/security-1.0.xsd"> | ||
|
||
<security:config> | ||
<!-- ... --> | ||
|
||
<security:encoder class="legacy" | ||
algorithm="sha256" | ||
encode-as-base64="false" | ||
iterations="1" | ||
/> | ||
|
||
<!-- algorithm: the new encoder, along with its options --> | ||
<security:encoder class="App\Entity\User" | ||
algorithm="sodium" | ||
> | ||
<!-- uses the bcrypt encoder with the default options --> | ||
<security:migrate-from>bcrypt</security:migrate-from> | ||
|
||
<!-- uses the legacy encoder configured above --> | ||
<security:migrate-from>legacy</security:migrate-from> | ||
</security:encoder> | ||
</security:config> | ||
</container> | ||
|
||
.. code-block:: php | ||
|
||
// config/packages/security.php | ||
$container->loadFromExtension('security', [ | ||
// ... | ||
|
||
'encoders' => [ | ||
'legacy' => [ | ||
'algorithm' => 'sha256', | ||
'encode_as_base64' => false, | ||
'iterations' => 1, | ||
], | ||
|
||
'App\Entity\User' => [ | ||
// the new encoder, along with its options | ||
'algorithm' => 'sodium', | ||
'migrate_from' => [ | ||
'bcrypt', // uses the "bcrypt" encoder with the default options | ||
'legacy', // uses the "legacy" encoder configured above | ||
], | ||
], | ||
], | ||
]); | ||
|
||
.. tip:: | ||
|
||
The *auto*, *native*, *bcrypt* and *argon* encoders automatically enable | ||
password migration using the following list of ``migrate_from`` algorithms: | ||
|
||
#. :ref:`PBKDF2 <reference-security-pbkdf2>` (which uses :phpfunction:`hash_pbkdf2`); | ||
#. Message digest (which uses :phpfunction:`hash`) | ||
|
||
Both use the ``hash_algorithm`` setting as algorithm. It is recommended to | ||
use ``migrate_from`` instead of ``hash_algorithm``, unless the *auto* | ||
encoder is used. | ||
|
||
Upgrade the Password | ||
-------------------- | ||
|
||
Upon successful login, the Security system checks whether a better algorithm | ||
is available to hash the user's password. If it is, it'll hash the correct | ||
password using the new hash. You can enable this behavior by implementing how | ||
this newly hashed password should be stored: | ||
|
||
* `When using Doctrine's entity user provider <Upgrade the Password when using Doctrine>`_ | ||
* `When using a custom user provider <Upgrade the Password when using a custom User Provider>`_ | ||
|
||
After this, you're done and passwords are always hashed as secure as possible! | ||
|
||
Upgrade the Password when using Doctrine | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
When using the :ref:`entity user provider <security-entity-user-provider>`, implement | ||
:class:`Symfony\\Component\\Security\\Core\\User\\PasswordUpgraderInterface` in | ||
the ``UserRepository`` (see `the Doctrine docs for information`_ on how to | ||
create this class if it's not already created). This interface implements | ||
storing the newly created password hash:: | ||
|
||
// src/Repository/UserRepository.php | ||
namespace App\Repository; | ||
|
||
// ... | ||
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; | ||
|
||
class UserRepository extends EntityRepository implements PasswordUpgraderInterface | ||
{ | ||
// ... | ||
|
||
public function upgradePassword(UserInterface $user, string $newEncodedPassword): void | ||
{ | ||
// set the new encoded password on the User object | ||
$user->setPassword($newEncodedPassword); | ||
|
||
// execute the queries on the database | ||
$this->getEntityManager()->flush($user); | ||
} | ||
} | ||
|
||
Upgrade the Password when using a Custom User Provider | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
If you're using a :ref:`custom user provider <custom-user-provider>`, implement the | ||
:class:`Symfony\\Component\\Security\\Core\\User\\PasswordUpgraderInterface` in | ||
the user provider:: | ||
|
||
// src/Security/UserProvider.php | ||
namespace App\Security; | ||
|
||
// ... | ||
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; | ||
|
||
class UserProvider implements UserProviderInterface, PasswordUpgraderInterface | ||
{ | ||
// ... | ||
|
||
public function upgradePassword(UserInterface $user, string $newEncodedPassword): void | ||
{ | ||
// set the new encoded password on the User object | ||
$user->setPassword($newEncodedPassword); | ||
|
||
// ... store the new password | ||
} | ||
} | ||
|
||
Trigger Password Migration From a Custom Encoder | ||
------------------------------------------------ | ||
|
||
If you're using a custom password encoder, you can trigger the password | ||
migration by returning ``true`` in the ``needsRehash()`` method:: | ||
|
||
// src/Security/UserProvider.php | ||
namespace App\Security; | ||
|
||
// ... | ||
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface; | ||
|
||
class CustomPasswordEncoder implements PasswordEncoderInterface | ||
{ | ||
// ... | ||
|
||
public function needsRehash(string $encoded): bool | ||
{ | ||
// check whether the current password is hash using an outdated encoder | ||
$hashIsOutdated = ...; | ||
|
||
return $hashIsOutdated; | ||
} | ||
} | ||
|
||
.. _`the Doctrine docs for information`: https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/working-with-objects.html#custom-repositories |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.