mirror of https://github.com/django/django.git
Fixed #26188 -- Documented how to wrap password hashers.
This commit is contained in:
parent
33a4040d07
commit
5a541e2e6c
|
@ -199,6 +199,89 @@ bcrypt rounds.
|
||||||
|
|
||||||
Passwords updates when changing the number of bcrypt rounds was added.
|
Passwords updates when changing the number of bcrypt rounds was added.
|
||||||
|
|
||||||
|
.. _wrapping-password-hashers:
|
||||||
|
|
||||||
|
Password upgrading without requiring a login
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
If you have an existing database with an older, weak hash such as MD5 or SHA1,
|
||||||
|
you might want to upgrade those hashes yourself instead of waiting for the
|
||||||
|
upgrade to happen when a user logs in (which may never happen if a user doesn't
|
||||||
|
return to your site). In this case, you can use a "wrapped" password hasher.
|
||||||
|
|
||||||
|
For this example, we'll migrate a collection of SHA1 hashes to use
|
||||||
|
PDKDF2(SHA1(password)) and add the corresponding password hasher for checking
|
||||||
|
if a user entered the correct password on login. We assume we're using the
|
||||||
|
built-in ``User`` model and that our project has an ``accounts`` app. You can
|
||||||
|
modify the pattern to work with any algorithm or with a custom user model.
|
||||||
|
|
||||||
|
First, we'll add the custom hasher:
|
||||||
|
|
||||||
|
.. snippet::
|
||||||
|
:filename: accounts/hashers.py
|
||||||
|
|
||||||
|
from django.contrib.auth.hashers import (
|
||||||
|
PBKDF2PasswordHasher, SHA1PasswordHasher,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PBKDF2WrappedSHA1PasswordHasher(PBKDF2PasswordHasher):
|
||||||
|
algorithm = 'pbkdf2_wrapped_sha1'
|
||||||
|
|
||||||
|
def encode_sha1_hash(self, sha1_hash, salt, iterations=None):
|
||||||
|
return super(PBKDF2WrappedSHA1PasswordHasher, self).encode(sha1_hash, salt, iterations)
|
||||||
|
|
||||||
|
def encode(self, password, salt, iterations=None):
|
||||||
|
_, _, sha1_hash = SHA1PasswordHasher().encode(password, salt).split('$', 2)
|
||||||
|
return self.encode_sha1_hash(sha1_hash, salt, iterations)
|
||||||
|
|
||||||
|
The data migration might look something like:
|
||||||
|
|
||||||
|
.. snippet::
|
||||||
|
:filename: accounts/migrations/0002_migrate_sha1_passwords.py
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
from ..hashers import PBKDF2WrappedSHA1PasswordHasher
|
||||||
|
|
||||||
|
|
||||||
|
def forwards_func(apps, schema_editor):
|
||||||
|
User = apps.get_model('auth', 'User')
|
||||||
|
users = User.objects.filter(password__startswith='sha1$')
|
||||||
|
hasher = PBKDF2WrappedSHA1PasswordHasher()
|
||||||
|
for user in users:
|
||||||
|
algorithm, salt, sha1_hash = user.password.split('$', 2)
|
||||||
|
user.password = hasher.encode_sha1_hash(sha1_hash, salt)
|
||||||
|
user.save(update_fields=['password'])
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('accounts', '0001_initial'),
|
||||||
|
# replace this with the latest migration in contrib.auth
|
||||||
|
('auth', '####_migration_name'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(forwards_func),
|
||||||
|
]
|
||||||
|
|
||||||
|
Be aware that this migration will take on the order of several minutes for
|
||||||
|
several thousand users, depending on the speed of your hardware.
|
||||||
|
|
||||||
|
Finally, we'll add a :setting:`PASSWORD_HASHERS` setting:
|
||||||
|
|
||||||
|
.. snippet::
|
||||||
|
:filename: mysite/settings.py
|
||||||
|
|
||||||
|
PASSWORD_HASHERS = [
|
||||||
|
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
|
||||||
|
'accounts.hashers.PBKDF2WrappedSHA1PasswordHasher',
|
||||||
|
]
|
||||||
|
|
||||||
|
Include any other hashers that your site uses in this list.
|
||||||
|
|
||||||
.. _sha1: https://en.wikipedia.org/wiki/SHA1
|
.. _sha1: https://en.wikipedia.org/wiki/SHA1
|
||||||
.. _pbkdf2: https://en.wikipedia.org/wiki/PBKDF2
|
.. _pbkdf2: https://en.wikipedia.org/wiki/PBKDF2
|
||||||
.. _nist: http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf
|
.. _nist: http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf
|
||||||
|
|
Loading…
Reference in New Issue