Author: PaulM Date: 2012-02-29 12:12:16 -0800 (Wed, 29 Feb 2012) New Revision: 17604
Modified: django/trunk/django/conf/global_settings.py django/trunk/django/contrib/auth/hashers.py django/trunk/django/contrib/auth/tests/hashers.py Log: Fixes #17777 and makes tests run again. Adds a salted MD5 hasher for backwards compatibility. Thanks [email protected] for the report. Also fixes a bug preventing the hasher tests from being run during contrib tests. Modified: django/trunk/django/conf/global_settings.py =================================================================== --- django/trunk/django/conf/global_settings.py 2012-02-29 17:46:23 UTC (rev 17603) +++ django/trunk/django/conf/global_settings.py 2012-02-29 20:12:16 UTC (rev 17604) @@ -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', ) Modified: django/trunk/django/contrib/auth/hashers.py =================================================================== --- django/trunk/django/contrib/auth/hashers.py 2012-02-29 17:46:23 UTC (rev 17603) +++ django/trunk/django/contrib/auth/hashers.py 2012-02-29 20:12:16 UTC (rev 17604) @@ -36,7 +36,7 @@ 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 @@ 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,6 +303,34 @@ 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. @@ -308,7 +338,7 @@ 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 '' Modified: django/trunk/django/contrib/auth/tests/hashers.py =================================================================== --- django/trunk/django/contrib/auth/tests/hashers.py 2012-02-29 17:46:23 UTC (rev 17603) +++ django/trunk/django/contrib/auth/tests/hashers.py 2012-02-29 20:12:16 UTC (rev 17604) @@ -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_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 @@ state['upgraded'] = True self.assertFalse(check_password('WRONG', encoded, setter)) self.assertFalse(state['upgraded']) - - -TestUtilsHashPass = override_settings(PASSWORD_HASHERS=default_hashers)(TestUtilsHashPass) -- You received this message because you are subscribed to the Google Groups "Django updates" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/django-updates?hl=en.
