Bump the default iterations for PBKDF2.

The rate at which we've increased this has not been keeping up with hardware (and software) improvements, and we're now considerably behind where we should be. The delta between our performance and an optimized implementation's performance prevents us from improving that further, but hopefully once Python 2.7.8 and 3.4+ get into more hands we can more aggressively increase this number.
This commit is contained in:
Alex Gaynor 2014-07-11 22:43:17 -07:00
parent ebb13bbd88
commit 6732566967
4 changed files with 14 additions and 12 deletions

View File

@ -222,12 +222,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 12000 iterations. Configured to use PBKDF2 + HMAC + SHA256 with 20000 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 = 12000 iterations = 20000
digest = hashlib.sha256 digest = hashlib.sha256
def encode(self, password, salt, iterations=None): def encode(self, password, salt, iterations=None):

View File

@ -47,7 +47,7 @@ class TestUtilsHashPass(SimpleTestCase):
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$12000$seasalt$Ybw8zsFxqja97tY/o6G+Fy1ksY4U/Hw3DRrGED6Up4s=') 'pbkdf2_sha256$20000$seasalt$oBSd886ysm3AqYun62DOdin8YcfbU1z9cksZSuLP9r0=')
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))
@ -211,14 +211,14 @@ class TestUtilsHashPass(SimpleTestCase):
hasher = PBKDF2PasswordHasher() hasher = PBKDF2PasswordHasher()
encoded = hasher.encode('lètmein', 'seasalt2') encoded = hasher.encode('lètmein', 'seasalt2')
self.assertEqual(encoded, self.assertEqual(encoded,
'pbkdf2_sha256$12000$seasalt2$hlDLKsxgkgb1aeOppkM5atCYw5rPzAjCNQZ4NYyUROw=') 'pbkdf2_sha256$20000$seasalt2$Flpve/uAcyo6+IFI6YAhjeABGPVbRQjzHDxRhqxewgw=')
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', 'seasalt2') encoded = hasher.encode('lètmein', 'seasalt2')
self.assertEqual(encoded, self.assertEqual(encoded,
'pbkdf2_sha1$12000$seasalt2$JeMRVfjjgtWw3/HzlnlfqBnQ6CA=') 'pbkdf2_sha1$20000$seasalt2$pJt86NmjAweBY1StBvxCu7l1o9o=')
self.assertTrue(hasher.verify('lètmein', encoded)) self.assertTrue(hasher.verify('lètmein', encoded))
def test_upgrade(self): def test_upgrade(self):

View File

@ -145,12 +145,14 @@ else:
HMAC+SHA256 is used as the default pseudo random function. HMAC+SHA256 is used as the default pseudo random function.
As of 2011, 10,000 iterations was the recommended default which As of 2014, 100,000 iterations was the recommended default which took
took 100ms on a 2.2Ghz Core 2 Duo. This is probably the bare 100ms on a 2.7Ghz Intel i7 with an optimized implementation. This is
minimum for security given 1000 iterations was recommended in probably the bare minimum for security given 1000 iterations was
2001. This code is very well optimized for CPython and is only recommended in 2001. This code is very well optimized for CPython and
four times slower than openssl's implementation. Look in is about five times slower than OpenSSL's implementation. Look in
django.contrib.auth.hashers for the present default. django.contrib.auth.hashers for the present default, it is lower than
the recommended 100,000 because of the performance difference between
this and an optimized implementation.
""" """
assert iterations > 0 assert iterations > 0
if not digest: if not digest:

View File

@ -91,7 +91,7 @@ any time leading up to the actual release:
#. If this is a major release, make sure the tests pass, then increase #. If this is a major release, make sure the tests pass, then increase
the default PBKDF2 iterations in the default PBKDF2 iterations in
``django.contrib.auth.hashers.PBKDF2PasswordHasher`` by about 10% ``django.contrib.auth.hashers.PBKDF2PasswordHasher`` by about 20%
(pick a round number). Run the tests, and update the 3 failing (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 hasher tests with the new values. Make sure this gets noted in the
release notes (see release notes on 1.6 for an example). release notes (see release notes on 1.6 for an example).