From 5b589a47b9ac2c8bdd13ba837fea37991a1457b9 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Mon, 26 Feb 2018 09:05:18 -0500 Subject: [PATCH] Fixed #29161 -- Removed BCryptPasswordHasher from PASSWORD_HASHERS. --- django/conf/global_settings.py | 1 - docs/ref/settings.txt | 1 - docs/releases/2.1.txt | 17 +++++++++++++++++ docs/topics/auth/passwords.txt | 29 ----------------------------- tests/auth_tests/test_hashers.py | 3 +++ 5 files changed, 20 insertions(+), 31 deletions(-) diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 938aae83471..8efa602e1e4 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -512,7 +512,6 @@ PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 'django.contrib.auth.hashers.Argon2PasswordHasher', 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', - 'django.contrib.auth.hashers.BCryptPasswordHasher', ] AUTH_PASSWORD_VALIDATORS = [] diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 152c6e339c3..bc36f7c1d13 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -2835,7 +2835,6 @@ Default:: 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 'django.contrib.auth.hashers.Argon2PasswordHasher', 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', - 'django.contrib.auth.hashers.BCryptPasswordHasher', ] .. setting:: AUTH_PASSWORD_VALIDATORS diff --git a/docs/releases/2.1.txt b/docs/releases/2.1.txt index 0a7d6da6cb8..65b41b2eedf 100644 --- a/docs/releases/2.1.txt +++ b/docs/releases/2.1.txt @@ -278,6 +278,23 @@ Dropped support for PostgreSQL 9.3 The end of upstream support for PostgreSQL 9.3 is September 2018. Django 2.1 supports PostgreSQL 9.4 and higher. +Removed ``BCryptPasswordHasher`` from the default ``PASSWORD_HASHERS`` setting +------------------------------------------------------------------------------ + +If you used bcrypt with Django 1.4 or 1.5 (before ``BCryptSHA256PasswordHasher`` +was added in Django 1.6), you might have some passwords that use the +``BCryptPasswordHasher`` hasher. + +You can check if that's the case like this:: + + from django.contrib.auth import get_user_model + User = get_user_model() + User.objects.filter(password__startswith='bcrypt$$') + +If you want to continue to allow those passwords to be used, you'll +have to define the :setting:`PASSWORD_HASHERS` setting (if you don't already) +and include ``'django.contrib.auth.hashers.BCryptPasswordHasher'``. + Miscellaneous ------------- diff --git a/docs/topics/auth/passwords.txt b/docs/topics/auth/passwords.txt index 6ae7f3d58ec..4dc6748075c 100644 --- a/docs/topics/auth/passwords.txt +++ b/docs/topics/auth/passwords.txt @@ -62,7 +62,6 @@ The default for :setting:`PASSWORD_HASHERS` is:: 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 'django.contrib.auth.hashers.Argon2PasswordHasher', 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', - 'django.contrib.auth.hashers.BCryptPasswordHasher', ] This means that Django will use PBKDF2_ to store all passwords but will support @@ -99,7 +98,6 @@ To use Argon2 as your default storage algorithm, do the following: 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', - 'django.contrib.auth.hashers.BCryptPasswordHasher', ] Keep and/or add any entries in this list if you need Django to :ref:`upgrade @@ -126,7 +124,6 @@ To use Bcrypt as your default storage algorithm, do the following: PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', - 'django.contrib.auth.hashers.BCryptPasswordHasher', 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 'django.contrib.auth.hashers.Argon2PasswordHasher', @@ -138,31 +135,6 @@ To use Bcrypt as your default storage algorithm, do the following: That's it -- now your Django install will use Bcrypt as the default storage algorithm. -.. admonition:: Password truncation with BCryptPasswordHasher - - The designers of bcrypt truncate all passwords at 72 characters which means - that ``bcrypt(password_with_100_chars) == bcrypt(password_with_100_chars[:72])``. - The original ``BCryptPasswordHasher`` does not have any special handling and - thus is also subject to this hidden password length limit. - ``BCryptSHA256PasswordHasher`` fixes this by first hashing the - password using sha256. This prevents the password truncation and so should - be preferred over the ``BCryptPasswordHasher``. The practical ramification - of this truncation is pretty marginal as the average user does not have a - password greater than 72 characters in length and even being truncated at 72 - the compute powered required to brute force bcrypt in any useful amount of - time is still astronomical. Nonetheless, we recommend you use - ``BCryptSHA256PasswordHasher`` anyway on the principle of "better safe than - sorry". - -.. admonition:: Other bcrypt implementations - - There are several other implementations that allow bcrypt to be - used with Django. Django's bcrypt support is NOT directly - compatible with these. To upgrade, you will need to modify the - hashes in your database to be in the form ``bcrypt$(raw bcrypt - output)``. For example: - ``bcrypt$$2a$12$NT0I31Sa7ihGEWpka9ASYrEFkhuTNeBQ2xfZskIiiJeyFXhRgS.Sy``. - .. _increasing-password-algorithm-work-factor: Increasing the work factor @@ -202,7 +174,6 @@ default PBKDF2 algorithm: 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 'django.contrib.auth.hashers.Argon2PasswordHasher', 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', - 'django.contrib.auth.hashers.BCryptPasswordHasher', ] That's it -- now your Django install will use more iterations when it diff --git a/tests/auth_tests/test_hashers.py b/tests/auth_tests/test_hashers.py index 109aa741535..51aaa3d72cb 100644 --- a/tests/auth_tests/test_hashers.py +++ b/tests/auth_tests/test_hashers.py @@ -172,6 +172,7 @@ class TestUtilsHashPass(SimpleTestCase): self.assertFalse(check_password(' ', blank_encoded)) @skipUnless(bcrypt, "bcrypt not installed") + @override_settings(PASSWORD_HASHERS=['django.contrib.auth.hashers.BCryptPasswordHasher']) def test_bcrypt(self): encoded = make_password('lètmein', hasher='bcrypt') self.assertTrue(is_password_usable(encoded)) @@ -187,6 +188,7 @@ class TestUtilsHashPass(SimpleTestCase): self.assertFalse(check_password(' ', blank_encoded)) @skipUnless(bcrypt, "bcrypt not installed") + @override_settings(PASSWORD_HASHERS=['django.contrib.auth.hashers.BCryptPasswordHasher']) def test_bcrypt_upgrade(self): hasher = get_hasher('bcrypt') self.assertEqual('bcrypt', hasher.algorithm) @@ -219,6 +221,7 @@ class TestUtilsHashPass(SimpleTestCase): hasher.rounds = old_rounds @skipUnless(bcrypt, "bcrypt not installed") + @override_settings(PASSWORD_HASHERS=['django.contrib.auth.hashers.BCryptPasswordHasher']) def test_bcrypt_harden_runtime(self): hasher = get_hasher('bcrypt') self.assertEqual('bcrypt', hasher.algorithm)