Loading docs/topics/auth/passwords.txt +83 −0 Original line number Diff line number Diff line Loading @@ -194,6 +194,89 @@ sure never to *remove* entries from this list. If you do, users using unmentioned algorithms won't be able to upgrade. Passwords will be upgraded when changing the PBKDF2 iteration count. .. _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 PBKDF2(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 .. _pbkdf2: https://en.wikipedia.org/wiki/PBKDF2 .. _nist: http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf Loading Loading
docs/topics/auth/passwords.txt +83 −0 Original line number Diff line number Diff line Loading @@ -194,6 +194,89 @@ sure never to *remove* entries from this list. If you do, users using unmentioned algorithms won't be able to upgrade. Passwords will be upgraded when changing the PBKDF2 iteration count. .. _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 PBKDF2(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 .. _pbkdf2: https://en.wikipedia.org/wiki/PBKDF2 .. _nist: http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf Loading