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 2f4e7c4

Browse filesBrowse files
committed
Added article on password migrations
1 parent d5ec360 commit 2f4e7c4
Copy full SHA for 2f4e7c4

File tree

2 files changed

+183
-0
lines changed
Filter options

2 files changed

+183
-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
@@ -987,6 +987,7 @@ Authentication (Identifying/Logging in the User)
987987

988988
security/form_login_setup
989989
security/guard_authentication
990+
security/migrating_password_hashes
990991
security/auth_providers
991992
security/user_provider
992993
security/ldap
+182Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
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 succesfully authenticated.
15+
16+
To enable this, make sure you apply the following steps to your application:
17+
18+
#. `Configure the "auto" Encoder`_
19+
#. `Upgrade the Password`_
20+
#. Optionally, `Trigger Password Migration From a Custom Encoder`_
21+
22+
Configure the "auto" Encoder
23+
----------------------------
24+
25+
The "auto" encoder is able to verify the hash of a number of different
26+
algorithms and can also hash new plain text passwords.
27+
28+
.. configuration-block::
29+
30+
.. code-block:: yaml
31+
32+
# config/packages/security.yaml
33+
security:
34+
# ...
35+
36+
encoders:
37+
App\Entity\User:
38+
algorithm: auto
39+
cost: 14
40+
41+
.. code-block:: xml
42+
43+
<!-- config/packages/security.xml -->
44+
<?xml version="1.0" encoding="UTF-8"?>
45+
<srv:container xmlns="http://symfony.com/schema/dic/security"
46+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
47+
xmlns:srv="http://symfony.com/schema/dic/services"
48+
xsi:schemaLocation="http://symfony.com/schema/dic/services
49+
https://symfony.com/schema/dic/services/services-1.0.xsd">
50+
51+
<config>
52+
<!-- ... -->
53+
54+
<encoder class="App\Entity\User"
55+
algorithm="auto"
56+
cost="14"/>
57+
58+
<!-- ... -->
59+
</config>
60+
</srv:container>
61+
62+
.. code-block:: php
63+
64+
// config/packages/security.php
65+
$container->loadFromExtension('security', [
66+
// ...
67+
68+
'encoders' => [
69+
'App\Entity\User' => [
70+
'algorithm' => 'auto',
71+
'cost' => 14,
72+
]
73+
],
74+
75+
// ...
76+
]);
77+
78+
The auto encoder does this by using a list of encoders. The first algorithm in
79+
the list is used to hash new passwords. It is configured to use the following algorithms:
80+
81+
#. :ref:`Sodium <reference-security-sodium>` when available, otherwise native is used (which uses :phpfunction:`password_hash`);
82+
#. :ref:`PBKDF2 <reference-security-pbkdf2>` (which uses :phpfunction:`hash_pbkdf2`);
83+
#. The configured encoder in ``hash_algorithm``.
84+
85+
If you previously used the bcrypt encoder, they are supported through the
86+
Sodium and Native encoders. Any custom encoder previously used can be
87+
configured by setting ``hash_algorithm`` to the custom encoder name.
88+
89+
Upgrade the Password
90+
--------------------
91+
92+
Upon successful login, the Security system checks whether a better algorithm
93+
is available to hash the user's password. If it is, it'll hash the correct
94+
password using the new hash. You can enable this behavior by implementing how
95+
this newly hashed password should be stored:
96+
97+
* `When using Doctrine's entity user provider <Upgrade the Password when using Doctrine>`_
98+
* `When using a custom user provider <Upgrade the Password when using a custom User Provider>`_
99+
100+
After this, you're done and passwords are always hashed as secure as possible!
101+
102+
Upgrade the Password when using Doctrine
103+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
104+
105+
When using the :ref:`entity user provider <security-entity-user-provider>`, implement
106+
:class:`Symfony\\Component\\Security\\Core\\User\\PasswordUpgraderInterface` in
107+
the ``UserRepository`` (see `the Doctrine docs for information`_ on how to
108+
create this class if it's not already created). This interface implements
109+
storing the newly created password hash::
110+
111+
// src/Repository/UserRepository.php
112+
namespace App\Repository;
113+
114+
// ...
115+
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
116+
117+
class UserRepository extends EntityRepository implements PasswordUpgraderInterface
118+
{
119+
// ...
120+
121+
public function upgradePassword(UserInterface $user, string $newEncodedPassword): void
122+
{
123+
// set the new encoded password on the User object
124+
$user->setPassword($newEncodedPassword);
125+
126+
// execute the queries on the database
127+
$this->getEntityManager->flush($user);
128+
}
129+
}
130+
131+
Upgrade the Password when using a custom User Provider
132+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
133+
134+
If you're using a :ref:`custom user provider <custom-user-provider>`, implement the
135+
:class:`Symfony\\Component\\Security\\Core\\User\\PasswordUpgraderInterface` in
136+
the user provider::
137+
138+
// src/Security/UserProvider.php
139+
namespace App\Security;
140+
141+
// ...
142+
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
143+
144+
class UserProvider implements UserProviderInterface, PasswordUpgraderInterface
145+
{
146+
// ...
147+
148+
public function upgradePassword(UserInterface $user, string $newEncodedPassword): void
149+
{
150+
// set the new encoded password on the User object
151+
$user->setPassword($newEncodedPassword);
152+
153+
// ... store the new password
154+
}
155+
}
156+
157+
Trigger Password Migration From a Custom Encoder
158+
------------------------------------------------
159+
160+
If you're using a custom password encoder, you can trigger the password
161+
migration by returning ``true`` in the ``needsRehash()`` method::
162+
163+
// src/Security/UserProvider.php
164+
namespace App\Security;
165+
166+
// ...
167+
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
168+
169+
class CustomPasswordEncoder implements PasswordEncoderInterface
170+
{
171+
// ...
172+
173+
public function needsRehash(string $encoded): bool
174+
{
175+
// check whether the current password is hash using an outdated encoder
176+
$hashIsOutdated = ...;
177+
178+
return $hashIsOutdated;
179+
}
180+
}
181+
182+
.. _`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.