Fixed #18144 -- Added backwards compatibility with old unsalted MD5 passwords

Thanks apreobrazhensky at gmail.com for the report.
This commit is contained in:
Claude Paroz 2013-02-02 11:57:25 +01:00
parent ace9d4efc3
commit 63d6a50dd8
2 changed files with 9 additions and 1 deletions

View File

@ -132,7 +132,8 @@ def identify_hasher(encoded):
get_hasher() to return hasher. Raises ValueError if get_hasher() to return hasher. Raises ValueError if
algorithm cannot be identified, or if hasher is not loaded. algorithm cannot be identified, or if hasher is not loaded.
""" """
if len(encoded) == 32 and '$' not in encoded: if ((len(encoded) == 32 and '$' not in encoded) or
len(encoded) == 37 and encoded.startswith('md5$$')):
algorithm = 'unsalted_md5' algorithm = 'unsalted_md5'
else: else:
algorithm = encoded.split('$', 1)[0] algorithm = encoded.split('$', 1)[0]
@ -372,6 +373,8 @@ class UnsaltedMD5PasswordHasher(BasePasswordHasher):
return hashlib.md5(force_bytes(password)).hexdigest() return hashlib.md5(force_bytes(password)).hexdigest()
def verify(self, password, encoded): def verify(self, password, encoded):
if len(encoded) == 37 and encoded.startswith('md5$$'):
encoded = encoded[5:]
encoded_2 = self.encode(password, '') encoded_2 = self.encode(password, '')
return constant_time_compare(encoded, encoded_2) return constant_time_compare(encoded, encoded_2)

View File

@ -66,6 +66,11 @@ class TestUtilsHashPass(unittest.TestCase):
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))
self.assertEqual(identify_hasher(encoded).algorithm, "unsalted_md5") self.assertEqual(identify_hasher(encoded).algorithm, "unsalted_md5")
# Alternate unsalted syntax
alt_encoded = "md5$$%s" % encoded
self.assertTrue(is_password_usable(alt_encoded))
self.assertTrue(check_password('lètmein', alt_encoded))
self.assertFalse(check_password('lètmeinz', alt_encoded))
@skipUnless(crypt, "no crypt module to generate password.") @skipUnless(crypt, "no crypt module to generate password.")
def test_crypt(self): def test_crypt(self):