summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaude Paroz <claude@2xlibre.net>2013-02-02 11:57:25 +0100
committerClaude Paroz <claude@2xlibre.net>2013-02-02 12:10:38 +0100
commit6bd3896fcb5c626a5ef613895d52c69130156d3a (patch)
tree9bd4a8e7c62cba425343bf7aec621e44899076b5
parent89ba1b27b4442cbb43555f607ab7d0f189a2af50 (diff)
[1.4.x] Fixed #18144 -- Added backwards compatibility with old unsalted MD5 passwords
Thanks apreobrazhensky at gmail.com for the report. Backport of 63d6a50dd from master.
-rw-r--r--django/contrib/auth/hashers.py5
-rw-r--r--django/contrib/auth/tests/hashers.py5
2 files changed, 9 insertions, 1 deletions
diff --git a/django/contrib/auth/hashers.py b/django/contrib/auth/hashers.py
index 58246852a8..1a93e8945b 100644
--- a/django/contrib/auth/hashers.py
+++ b/django/contrib/auth/hashers.py
@@ -35,7 +35,8 @@ def check_password(password, encoded, setter=None, preferred='default'):
password = smart_str(password)
encoded = smart_str(encoded)
- if len(encoded) == 32 and '$' not in encoded:
+ if ((len(encoded) == 32 and '$' not in encoded) or
+ (len(encoded) == 37 and encoded.startswith('md5$$'))):
hasher = get_hasher('unsalted_md5')
else:
algorithm = encoded.split('$', 1)[0]
@@ -347,6 +348,8 @@ class UnsaltedMD5PasswordHasher(BasePasswordHasher):
return hashlib.md5(password).hexdigest()
def verify(self, password, encoded):
+ if len(encoded) == 37 and encoded.startswith('md5$$'):
+ encoded = encoded[5:]
encoded_2 = self.encode(password, '')
return constant_time_compare(encoded, encoded_2)
diff --git a/django/contrib/auth/tests/hashers.py b/django/contrib/auth/tests/hashers.py
index 865085a194..6203e9a899 100644
--- a/django/contrib/auth/tests/hashers.py
+++ b/django/contrib/auth/tests/hashers.py
@@ -59,6 +59,11 @@ class TestUtilsHashPass(unittest.TestCase):
self.assertTrue(is_password_usable(encoded))
self.assertTrue(check_password(u'letmein', encoded))
self.assertFalse(check_password('letmeinz', encoded))
+ # Alternate unsalted syntax
+ alt_encoded = "md5$$%s" % encoded
+ self.assertTrue(is_password_usable(alt_encoded))
+ self.assertTrue(check_password(u'letmein', alt_encoded))
+ self.assertFalse(check_password('letmeinz', alt_encoded))
@skipUnless(crypt, "no crypt module to generate password.")
def test_crypt(self):