8000 Added article on password migrations · symfony/symfony-docs@0de1588 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0de1588

Browse files
committed
Added article on password migrations
1 parent a129ed7 commit 0de1588

File tree

2 files changed

+211
-0
lines changed

2 files changed

+211
-0
lines changed

security.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,7 @@ Authentication (Identifying/Logging in the User)
10001000
security/form_login_setup
10011001
security/json_login_setup
10021002
security/guard_authentication
1003+
security/password_migration
10031004
security/auth_providers
10041005
security/user_provider
10051006
security/ldap

security/password_migration.rst

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
.. index::
2+
single: Security; How to Migrate a Password Hash
3+
4+
How to Migrate a Password Hash
5+
==============================
6+
7+
.. versionadded:: 4.4
8+
9+
Password migration was introduced in Symfony 4.4.
10+
11+
In order to protect passwords, it is recommended to store them using the latest
12+
hash algorithms. This means that if a better hash algorithm is supported on the
13+
system, the user's password should be rehashed and stored. Symfony provides this
14+
functionality when a user is successfully authenticated.
15+
16+
To enable this, make sure you apply the following steps to your application:
17+
18+
#. `Configure a new Encoder Using "migrate_from"`_
19+
#. `Upgrade the Password`_
20+
#. Optionally, `Trigger Password Migration From a Custom Encoder`_
21+
22+
Configure a new Encoder Using "migrate_from"
23+
--------------------------------------------
24+
25+
When configuring a new encoder, you can specify a list of legacy encoders by
26+
using the ``migrate_from`` option:
27+
28+
.. configuration-block::
29+
30+
.. code-block:: yaml
31+
32+
# config/packages/security.yaml
33+
security:
34+
# ...
35+
36+
encoders:
37+
legacy:
38+
algorithm: sha256
39+
encode_as_base64: false
40+
iterations: 1
41+
42+
App\Entity\User:
43+
# the new encoder, along with its options
44+
algorithm: sodium
45+
migrate_from:
46+
- bcrypt # uses the "bcrypt" encoder with the default options
47+
- legacy # uses the "legacy" encoder configured above
48+
49+
.. code-block:: xml
50+
51+
<!-- config/packages/security.xml -->
52+
<?xml version="1.0" encoding="UTF-8"?>
53+
<container xmlns="http://symfony.com/schema/dic/services"
54+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
55+
xmlns:security="http://symfony.com/schema/dic/security"
56+
xsi:schemaLocation="http://symfony.com/schema/dic/security
57+
https://symfony.com/schema/dic/security/security-1.0.xsd">
58+
59+
<security:config>
60+
<!-- ... -->
61+
62+
<security:encoder class="legacy"
63+
algorithm="sha256"
64+
encode-as-base64="false"
65+
iterations="1"
66+
/>
67+
68+
<!-- algorithm: the new encoder, along with its options -->
69+
<security:encoder class="App\Entity\User"
70+
algorithm="sodium"
71+
>
72+
<!-- uses the bcrypt encoder with the default options -->
73+
<security:migrate-from>bcrypt</security:migrate-from>
74+
75+
<!-- uses the legacy encoder configured above -->
76+
<security:migrate-from>legacy</security:migrate-from>
77+
</security:encoder>
78+
</security:config>
79+
</container>
80+
81+
.. code-block:: php
82+
83+
// config/packages/security.php
84+
$container->loadFromExtension('security', [
85+
// ...
86+
87+
'encoders' => [
88+
'legacy' => [
89+
'algorithm' => 'sha256',
90+
'encode_as_base64' => false,
91+
'iterations' => 1,
92+
],
93+
94+
'App\Entity\User' => [
95+
// the new encoder, along with its options
96+
'algorithm' => 'sodium',
97+
'migrate_from' => [
98+
'bcrypt', // uses the "bcrypt" encoder with the default options
99+
'legacy', // uses the "legacy" encoder configured above
100+
],
101+
],
102+
],
103+
]);
104+
105+
.. tip::
106+
107+
The *auto*, *native*, *bcrypt* and *argon* encoders automatically enable
108+
password migration using the following list of ``migrate_from`` algorithms:
109+
110+
#. :ref:`PBKDF2 <reference-security-pbkdf2>` (which uses :phpfunction:`hash_pbkdf2`);
111+
#. Message digest (which uses :phpfunction:`hash`)
112+
113+
Both use the ``hash_algorithm`` setting as algorithm. It is recommended to
114+
use ``migrate_from`` instead of ``hash_algorithm``, unless the *auto*
115+
encoder is used.
116+
117+
Upgrade the Password
118+
--------------------
119+
120+
Upon successful login, the Security system checks whether a better algorithm
121+
is available to hash the user's password. If it is, it'll hash the correct
122+
password using the new hash. You can enable this behavior by implementing how
123+
this newly hashed password should be stored:
124+
125+
* `When using Doctrine's entity user provider <Upgrade the Password when using Doctrine>`_
126+
* `When using a custom user provider <Upgrade the Password when using a custom User Provider>`_
127+
128+
After this, you're done and passwords are always hashed as secure as possible!
129+
130+
Upgrade the Password when using Doctrine
131+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
132+
133+
When using the :ref:`entity user provider <security-entity-user-provider>`, implement
134+
:class:`Symfony\\Component\\Security\\Core\\User\\PasswordUpgraderInterface` in
135+
the ``UserRepository`` (see `the Doctrine docs for information`_ on how to
136+
create this class if it's not already created). This interface implements
137+
storing the newly created password hash::
138+
139+
// src/Repository/UserRepository.php
140+
namespace App\Repository;
141+
142+
// ...
143+
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
144+
145+
class UserRepository extends EntityRepository implements PasswordUpgraderInterface
146+
{
147+
// ...
148+
149+
public function upgradePassword(UserInterface $user, string $newEncodedPassword): void
150+
{
151+
// set the new encoded password on the User object
152+
$user->setPassword($newEncodedPassword);
153+
154+
// execute the queries on the database
155+
$this->getEntityManager()->flush($user);
156+
}
157+
}
158+
159+
Upgrade the Password when using a Custom User Provider
160+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
161+
162+
If you're using a :ref:`custom user provider <custom-user-provider>`, implement the
163+
:class:`Symfony\\Component\\Security\\Core\\User\\PasswordUpgraderInterface` in
164+
the user provider::
165+
166+
// src/Security/UserProvider.php
167+
namespace App\Security;
168+
169+
// ...
170+
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
171+
172+
class UserProvider implements UserProviderInterface, PasswordUpgraderInterface
173+
{
174+
// ...
175+
176+
public function upgradePassword(UserInterface $user, string $newEncodedPassword): void
177+
{
178+
// set the new encoded password on the User object
179+
$user->setPassword($newEncodedPassword);
180+
181+
// ... store the new password
182+
}
183+
}
184+
185+
Trigger Password Migration From a Custom Encoder
186+
------------------------------------------------
187+
188+
If you're using a custom password encoder, you can trigger the password
189+
migration by returning ``true`` in the ``needsRehash()`` method::
190+
191+
// src/Security/UserProvider.php
192+
namespace App\Security;
193+
194+
// ...
195+
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
196+
197+
class CustomPasswordEncoder implements PasswordEncoderInterface
198+
{
199+
// ...
200+
201+
public function needsRehash(string $encoded): bool
202+
{
203+
// check whether the current password is hash using an outdated encoder
204+
$hashIsOutdated = ...;
205+
206+
return $hashIsOutdated;
207+
}
208+
}
209+
210+
.. _`the Doctrine docs for information`: https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/working-with-objects.html#custom-repositories

0 commit comments

Comments
 (0)
0