Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 09aee36

Browse filesBrowse files
committed
feature #12467 [Security] Added article on password migrations (wouterj)
This PR was merged into the 4.4 branch. Discussion ---------- [Security] Added article on password migrations Fixes #12395, #12612 Please note that I did not yet test the code examples in this article. #SymfonyHackathon Commits ------- 0de1588 Added article on password migrations
2 parents 01e2f35 + 0de1588 commit 09aee36
Copy full SHA for 09aee36

File tree

2 files changed

+211
-0
lines changed
Filter options

2 files changed

+211
-0
lines changed

‎security.rst

Copy file name to clipboardExpand all lines: security.rst
+1Lines 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

Copy file name to clipboard
+210Lines 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)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.