Increase default PBKDF2 iterations

Increases the default PBKDF2 iterations, since computers have gotten
faster since 2011. In the future, we plan to increment by 10% per
major version.
This commit is contained in:
Paul McMillan 2013-09-19 17:39:43 +01:00
parent 59a34c43a8
commit a075e2ad0d
5 changed files with 27 additions and 12 deletions

View File

@ -231,12 +231,12 @@ class PBKDF2PasswordHasher(BasePasswordHasher):
""" """
Secure password hashing using the PBKDF2 algorithm (recommended) Secure password hashing using the PBKDF2 algorithm (recommended)
Configured to use PBKDF2 + HMAC + SHA256 with 10000 iterations. Configured to use PBKDF2 + HMAC + SHA256 with 12000 iterations.
The result is a 64 byte binary string. Iterations may be changed The result is a 64 byte binary string. Iterations may be changed
safely but you must rename the algorithm if you change SHA256. safely but you must rename the algorithm if you change SHA256.
""" """
algorithm = "pbkdf2_sha256" algorithm = "pbkdf2_sha256"
iterations = 10000 iterations = 12000
digest = hashlib.sha256 digest = hashlib.sha256
@password_max_length(MAXIMUM_PASSWORD_LENGTH) @password_max_length(MAXIMUM_PASSWORD_LENGTH)

View File

@ -52,7 +52,7 @@ class TestUtilsHashPass(unittest.TestCase):
def test_pkbdf2(self): def test_pkbdf2(self):
encoded = make_password('lètmein', 'seasalt', 'pbkdf2_sha256') encoded = make_password('lètmein', 'seasalt', 'pbkdf2_sha256')
self.assertEqual(encoded, self.assertEqual(encoded,
'pbkdf2_sha256$10000$seasalt$CWWFdHOWwPnki7HvkcqN9iA2T3KLW1cf2uZ5kvArtVY=') 'pbkdf2_sha256$12000$seasalt$Ybw8zsFxqja97tY/o6G+Fy1ksY4U/Hw3DRrGED6Up4s=')
self.assertTrue(is_password_usable(encoded)) self.assertTrue(is_password_usable(encoded))
self.assertTrue(check_password('lètmein', encoded)) self.assertTrue(check_password('lètmein', encoded))
self.assertFalse(check_password('lètmeinz', encoded)) self.assertFalse(check_password('lètmeinz', encoded))
@ -284,16 +284,16 @@ class TestUtilsHashPass(unittest.TestCase):
def test_low_level_pkbdf2(self): def test_low_level_pkbdf2(self):
hasher = PBKDF2PasswordHasher() hasher = PBKDF2PasswordHasher()
encoded = hasher.encode('lètmein', 'seasalt') encoded = hasher.encode('lètmein', 'seasalt2')
self.assertEqual(encoded, self.assertEqual(encoded,
'pbkdf2_sha256$10000$seasalt$CWWFdHOWwPnki7HvkcqN9iA2T3KLW1cf2uZ5kvArtVY=') 'pbkdf2_sha256$12000$seasalt2$hlDLKsxgkgb1aeOppkM5atCYw5rPzAjCNQZ4NYyUROw=')
self.assertTrue(hasher.verify('lètmein', encoded)) self.assertTrue(hasher.verify('lètmein', encoded))
def test_low_level_pbkdf2_sha1(self): def test_low_level_pbkdf2_sha1(self):
hasher = PBKDF2SHA1PasswordHasher() hasher = PBKDF2SHA1PasswordHasher()
encoded = hasher.encode('lètmein', 'seasalt') encoded = hasher.encode('lètmein', 'seasalt2')
self.assertEqual(encoded, self.assertEqual(encoded,
'pbkdf2_sha1$10000$seasalt$oAfF6vgs95ncksAhGXOWf4Okq7o=') 'pbkdf2_sha1$12000$seasalt2$JeMRVfjjgtWw3/HzlnlfqBnQ6CA=')
self.assertTrue(hasher.verify('lètmein', encoded)) self.assertTrue(hasher.verify('lètmein', encoded))
def test_upgrade(self): def test_upgrade(self):

View File

@ -139,11 +139,12 @@ def pbkdf2(password, salt, iterations, dklen=0, digest=None):
HMAC+SHA256 is used as the default pseudo random function. HMAC+SHA256 is used as the default pseudo random function.
Right now 10,000 iterations is the recommended default which takes As of 2011, 10,000 iterations was the recommended default which
100ms on a 2.2Ghz Core 2 Duo. This is probably the bare minimum took 100ms on a 2.2Ghz Core 2 Duo. This is probably the bare
for security given 1000 iterations was recommended in 2001. This minimum for security given 1000 iterations was recommended in
code is very well optimized for CPython and is only four times 2001. This code is very well optimized for CPython and is only
slower than openssl's implementation. four times slower than openssl's implementation. Look in
django.contrib.auth.hashers for the present default.
""" """
assert iterations > 0 assert iterations > 0
if not digest: if not digest:

View File

@ -89,6 +89,13 @@ any time leading up to the actual release:
key you'll use for the release, and should include patches for each issue key you'll use for the release, and should include patches for each issue
being fixed. being fixed.
#. If this is a major release, make sure the tests pass, then increase
the default PBKDF2 iterations in
``django.contrib.auth.hashers.PBKDF2PasswordHasher`` by about 10%
(pick a round number). Run the tests, and update the 3 failing
hasher tests with the new values. Make sure this gets noted in the
release notes (see release notes on 1.6 for an example).
#. As the release approaches, watch Trac to make sure no release blockers #. As the release approaches, watch Trac to make sure no release blockers
are left for the upcoming release. are left for the upcoming release.

View File

@ -365,6 +365,13 @@ Minor features
a list (except on SQLite). This has long been possible (but not officially a list (except on SQLite). This has long been possible (but not officially
supported) on MySQL and PostgreSQL, and is now also available on Oracle. supported) on MySQL and PostgreSQL, and is now also available on Oracle.
* The default iteration count for the PBKDF2 password hasher has been
increased by 20%. This backwards compatible change will not affect
existing passwords or users who have subclassed
`django.contrib.auth.hashers.PBKDF2PasswordHasher`` to change the
default value.
Backwards incompatible changes in 1.6 Backwards incompatible changes in 1.6
===================================== =====================================