Permalink
Browse files

Fixes #17777 and makes tests run again.

Adds a salted MD5 hasher for backwards compatibility.
Thanks gunnar@g10f.de for the report.

Also fixes a bug preventing the hasher tests from being run during
contrib tests.


git-svn-id: http://code.djangoproject.com/svn/django/trunk@17604 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent ae640e5 commit 413e37481d0b81d50b5826f660eeb79f360be9fc @PaulMcMillan PaulMcMillan committed Feb 29, 2012
Showing with 44 additions and 8 deletions.
  1. +1 −0 django/conf/global_settings.py
  2. +34 −4 django/contrib/auth/hashers.py
  3. +9 −4 django/contrib/auth/tests/hashers.py
@@ -507,6 +507,7 @@
'django.contrib.auth.hashers.BCryptPasswordHasher',
'django.contrib.auth.hashers.SHA1PasswordHasher',
'django.contrib.auth.hashers.MD5PasswordHasher',
+ 'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher',
'django.contrib.auth.hashers.CryptPasswordHasher',
)
@@ -36,7 +36,7 @@ def check_password(password, encoded, setter=None, preferred='default'):
encoded = smart_str(encoded)
if len(encoded) == 32 and '$' not in encoded:
- hasher = get_hasher('md5')
+ hasher = get_hasher('unsalted_md5')
else:
algorithm = encoded.split('$', 1)[0]
hasher = get_hasher(algorithm)
@@ -69,11 +69,13 @@ def make_password(password, salt=None, hasher='default'):
return hasher.encode(password, salt)
-def load_hashers():
+def load_hashers(password_hashers=None):
global HASHERS
global PREFERRED_HASHER
hashers = []
- for backend in settings.PASSWORD_HASHERS:
+ if not password_hashers:
+ password_hashers = settings.PASSWORD_HASHERS
+ for backend in password_hashers:
try:
mod_path, cls_name = backend.rsplit('.', 1)
mod = importlib.import_module(mod_path)
@@ -301,14 +303,42 @@ def safe_summary(self, encoded):
class MD5PasswordHasher(BasePasswordHasher):
"""
+ The Salted MD5 password hashing algorithm (not recommended)
+ """
+ algorithm = "md5"
+
+ def encode(self, password, salt):
+ assert password
+ assert salt and '$' not in salt
+ hash = hashlib.md5(salt + password).hexdigest()
+ return "%s$%s$%s" % (self.algorithm, salt, hash)
+
+ def verify(self, password, encoded):
+ algorithm, salt, hash = encoded.split('$', 2)
+ assert algorithm == self.algorithm
+ encoded_2 = self.encode(password, salt)
+ return constant_time_compare(encoded, encoded_2)
+
+ def safe_summary(self, encoded):
+ algorithm, salt, hash = encoded.split('$', 2)
+ assert algorithm == self.algorithm
+ return SortedDict([
+ (_('algorithm'), algorithm),
+ (_('salt'), mask_hash(salt, show=2)),
+ (_('hash'), mask_hash(hash)),
+ ])
+
+
+class UnsaltedMD5PasswordHasher(BasePasswordHasher):
+ """
I am an incredibly insecure algorithm you should *never* use;
stores unsalted MD5 hashes without the algorithm prefix.
This class is implemented because Django used to store passwords
this way. Some older Django installs still have these values
lingering around so we need to handle and upgrade them properly.
"""
- algorithm = "md5"
+ algorithm = "unsalted_md5"
def salt(self):
return ''
@@ -20,7 +20,7 @@
class TestUtilsHashPass(unittest.TestCase):
def setUp(self):
- load_hashers()
+ load_hashers(password_hashers=default_hashers)
def test_simple(self):
encoded = make_password('letmein')
@@ -47,6 +47,14 @@ def test_sha1(self):
def test_md5(self):
encoded = make_password('letmein', 'seasalt', 'md5')
+ self.assertEqual(encoded,
+ 'md5$seasalt$f5531bef9f3687d0ccf0f617f0e25573')
+ self.assertTrue(is_password_usable(encoded))
+ self.assertTrue(check_password(u'letmein', encoded))
+ self.assertFalse(check_password('letmeinz', encoded))
+
+ def test_unsalted_md5(self):
+ encoded = make_password('letmein', 'seasalt', 'unsalted_md5')
self.assertEqual(encoded, '0d107d09f5bbe40cade3de5c71e9e9b7')
self.assertTrue(is_password_usable(encoded))
self.assertTrue(check_password(u'letmein', encoded))
@@ -123,6 +131,3 @@ def setter():
state['upgraded'] = True
self.assertFalse(check_password('WRONG', encoded, setter))
self.assertFalse(state['upgraded'])
-
-
-TestUtilsHashPass = override_settings(PASSWORD_HASHERS=default_hashers)(TestUtilsHashPass)

0 comments on commit 413e374

Please sign in to comment.