mirror of https://github.com/django/django.git
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
This commit is contained in:
parent
ae640e5ea0
commit
413e37481d
|
@ -507,6 +507,7 @@ PASSWORD_HASHERS = (
|
||||||
'django.contrib.auth.hashers.BCryptPasswordHasher',
|
'django.contrib.auth.hashers.BCryptPasswordHasher',
|
||||||
'django.contrib.auth.hashers.SHA1PasswordHasher',
|
'django.contrib.auth.hashers.SHA1PasswordHasher',
|
||||||
'django.contrib.auth.hashers.MD5PasswordHasher',
|
'django.contrib.auth.hashers.MD5PasswordHasher',
|
||||||
|
'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher',
|
||||||
'django.contrib.auth.hashers.CryptPasswordHasher',
|
'django.contrib.auth.hashers.CryptPasswordHasher',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ def check_password(password, encoded, setter=None, preferred='default'):
|
||||||
encoded = smart_str(encoded)
|
encoded = smart_str(encoded)
|
||||||
|
|
||||||
if len(encoded) == 32 and '$' not in encoded:
|
if len(encoded) == 32 and '$' not in encoded:
|
||||||
hasher = get_hasher('md5')
|
hasher = get_hasher('unsalted_md5')
|
||||||
else:
|
else:
|
||||||
algorithm = encoded.split('$', 1)[0]
|
algorithm = encoded.split('$', 1)[0]
|
||||||
hasher = get_hasher(algorithm)
|
hasher = get_hasher(algorithm)
|
||||||
|
@ -69,11 +69,13 @@ def make_password(password, salt=None, hasher='default'):
|
||||||
return hasher.encode(password, salt)
|
return hasher.encode(password, salt)
|
||||||
|
|
||||||
|
|
||||||
def load_hashers():
|
def load_hashers(password_hashers=None):
|
||||||
global HASHERS
|
global HASHERS
|
||||||
global PREFERRED_HASHER
|
global PREFERRED_HASHER
|
||||||
hashers = []
|
hashers = []
|
||||||
for backend in settings.PASSWORD_HASHERS:
|
if not password_hashers:
|
||||||
|
password_hashers = settings.PASSWORD_HASHERS
|
||||||
|
for backend in password_hashers:
|
||||||
try:
|
try:
|
||||||
mod_path, cls_name = backend.rsplit('.', 1)
|
mod_path, cls_name = backend.rsplit('.', 1)
|
||||||
mod = importlib.import_module(mod_path)
|
mod = importlib.import_module(mod_path)
|
||||||
|
@ -300,6 +302,34 @@ class SHA1PasswordHasher(BasePasswordHasher):
|
||||||
|
|
||||||
|
|
||||||
class MD5PasswordHasher(BasePasswordHasher):
|
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;
|
I am an incredibly insecure algorithm you should *never* use;
|
||||||
stores unsalted MD5 hashes without the algorithm prefix.
|
stores unsalted MD5 hashes without the algorithm prefix.
|
||||||
|
@ -308,7 +338,7 @@ class MD5PasswordHasher(BasePasswordHasher):
|
||||||
this way. Some older Django installs still have these values
|
this way. Some older Django installs still have these values
|
||||||
lingering around so we need to handle and upgrade them properly.
|
lingering around so we need to handle and upgrade them properly.
|
||||||
"""
|
"""
|
||||||
algorithm = "md5"
|
algorithm = "unsalted_md5"
|
||||||
|
|
||||||
def salt(self):
|
def salt(self):
|
||||||
return ''
|
return ''
|
||||||
|
|
|
@ -20,7 +20,7 @@ except ImportError:
|
||||||
|
|
||||||
class TestUtilsHashPass(unittest.TestCase):
|
class TestUtilsHashPass(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
load_hashers()
|
load_hashers(password_hashers=default_hashers)
|
||||||
|
|
||||||
def test_simple(self):
|
def test_simple(self):
|
||||||
encoded = make_password('letmein')
|
encoded = make_password('letmein')
|
||||||
|
@ -47,6 +47,14 @@ class TestUtilsHashPass(unittest.TestCase):
|
||||||
|
|
||||||
def test_md5(self):
|
def test_md5(self):
|
||||||
encoded = make_password('letmein', 'seasalt', 'md5')
|
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.assertEqual(encoded, '0d107d09f5bbe40cade3de5c71e9e9b7')
|
||||||
self.assertTrue(is_password_usable(encoded))
|
self.assertTrue(is_password_usable(encoded))
|
||||||
self.assertTrue(check_password(u'letmein', encoded))
|
self.assertTrue(check_password(u'letmein', encoded))
|
||||||
|
@ -123,6 +131,3 @@ class TestUtilsHashPass(unittest.TestCase):
|
||||||
state['upgraded'] = True
|
state['upgraded'] = True
|
||||||
self.assertFalse(check_password('WRONG', encoded, setter))
|
self.assertFalse(check_password('WRONG', encoded, setter))
|
||||||
self.assertFalse(state['upgraded'])
|
self.assertFalse(state['upgraded'])
|
||||||
|
|
||||||
|
|
||||||
TestUtilsHashPass = override_settings(PASSWORD_HASHERS=default_hashers)(TestUtilsHashPass)
|
|
||||||
|
|
Loading…
Reference in New Issue