Refs #32508 -- Raised TypeError/ValueError instead of using "assert" in encode() methods of some password hashers.
This commit is contained in:
parent
c35b81b864
commit
83022d279c
|
@ -209,6 +209,12 @@ class BasePasswordHasher:
|
||||||
"""Check if the given password is correct."""
|
"""Check if the given password is correct."""
|
||||||
raise NotImplementedError('subclasses of BasePasswordHasher must provide a verify() method')
|
raise NotImplementedError('subclasses of BasePasswordHasher must provide a verify() method')
|
||||||
|
|
||||||
|
def _check_encode_args(self, password, salt):
|
||||||
|
if password is None:
|
||||||
|
raise TypeError('password must be provided.')
|
||||||
|
if not salt or '$' in salt:
|
||||||
|
raise ValueError('salt must be provided and cannot contain $.')
|
||||||
|
|
||||||
def encode(self, password, salt):
|
def encode(self, password, salt):
|
||||||
"""
|
"""
|
||||||
Create an encoded database value.
|
Create an encoded database value.
|
||||||
|
@ -269,8 +275,7 @@ class PBKDF2PasswordHasher(BasePasswordHasher):
|
||||||
digest = hashlib.sha256
|
digest = hashlib.sha256
|
||||||
|
|
||||||
def encode(self, password, salt, iterations=None):
|
def encode(self, password, salt, iterations=None):
|
||||||
assert password is not None
|
self._check_encode_args(password, salt)
|
||||||
assert salt and '$' not in salt
|
|
||||||
iterations = iterations or self.iterations
|
iterations = iterations or self.iterations
|
||||||
hash = pbkdf2(password, salt, iterations, digest=self.digest)
|
hash = pbkdf2(password, salt, iterations, digest=self.digest)
|
||||||
hash = base64.b64encode(hash).decode('ascii').strip()
|
hash = base64.b64encode(hash).decode('ascii').strip()
|
||||||
|
@ -519,8 +524,7 @@ class SHA1PasswordHasher(BasePasswordHasher):
|
||||||
algorithm = "sha1"
|
algorithm = "sha1"
|
||||||
|
|
||||||
def encode(self, password, salt):
|
def encode(self, password, salt):
|
||||||
assert password is not None
|
self._check_encode_args(password, salt)
|
||||||
assert salt and '$' not in salt
|
|
||||||
hash = hashlib.sha1((salt + password).encode()).hexdigest()
|
hash = hashlib.sha1((salt + password).encode()).hexdigest()
|
||||||
return "%s$%s$%s" % (self.algorithm, salt, hash)
|
return "%s$%s$%s" % (self.algorithm, salt, hash)
|
||||||
|
|
||||||
|
@ -561,8 +565,7 @@ class MD5PasswordHasher(BasePasswordHasher):
|
||||||
algorithm = "md5"
|
algorithm = "md5"
|
||||||
|
|
||||||
def encode(self, password, salt):
|
def encode(self, password, salt):
|
||||||
assert password is not None
|
self._check_encode_args(password, salt)
|
||||||
assert salt and '$' not in salt
|
|
||||||
hash = hashlib.md5((salt + password).encode()).hexdigest()
|
hash = hashlib.md5((salt + password).encode()).hexdigest()
|
||||||
return "%s$%s$%s" % (self.algorithm, salt, hash)
|
return "%s$%s$%s" % (self.algorithm, salt, hash)
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,9 @@ from django.conf.global_settings import PASSWORD_HASHERS
|
||||||
from django.contrib.auth.hashers import (
|
from django.contrib.auth.hashers import (
|
||||||
UNUSABLE_PASSWORD_PREFIX, UNUSABLE_PASSWORD_SUFFIX_LENGTH,
|
UNUSABLE_PASSWORD_PREFIX, UNUSABLE_PASSWORD_SUFFIX_LENGTH,
|
||||||
BasePasswordHasher, BCryptPasswordHasher, BCryptSHA256PasswordHasher,
|
BasePasswordHasher, BCryptPasswordHasher, BCryptSHA256PasswordHasher,
|
||||||
PBKDF2PasswordHasher, PBKDF2SHA1PasswordHasher, check_password, get_hasher,
|
MD5PasswordHasher, PBKDF2PasswordHasher, PBKDF2SHA1PasswordHasher,
|
||||||
identify_hasher, is_password_usable, make_password,
|
SHA1PasswordHasher, check_password, get_hasher, identify_hasher,
|
||||||
|
is_password_usable, make_password,
|
||||||
)
|
)
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
|
@ -474,6 +475,35 @@ class TestUtilsHashPass(SimpleTestCase):
|
||||||
check_password('wrong_password', encoded)
|
check_password('wrong_password', encoded)
|
||||||
self.assertEqual(hasher.harden_runtime.call_count, 1)
|
self.assertEqual(hasher.harden_runtime.call_count, 1)
|
||||||
|
|
||||||
|
def test_encode_invalid_salt(self):
|
||||||
|
hasher_classes = [
|
||||||
|
MD5PasswordHasher,
|
||||||
|
PBKDF2PasswordHasher,
|
||||||
|
PBKDF2SHA1PasswordHasher,
|
||||||
|
SHA1PasswordHasher,
|
||||||
|
]
|
||||||
|
msg = 'salt must be provided and cannot contain $.'
|
||||||
|
for hasher_class in hasher_classes:
|
||||||
|
hasher = hasher_class()
|
||||||
|
for salt in [None, '', 'sea$salt']:
|
||||||
|
with self.subTest(hasher_class.__name__, salt=salt):
|
||||||
|
with self.assertRaisesMessage(ValueError, msg):
|
||||||
|
hasher.encode('password', salt)
|
||||||
|
|
||||||
|
def test_encode_password_required(self):
|
||||||
|
hasher_classes = [
|
||||||
|
MD5PasswordHasher,
|
||||||
|
PBKDF2PasswordHasher,
|
||||||
|
PBKDF2SHA1PasswordHasher,
|
||||||
|
SHA1PasswordHasher,
|
||||||
|
]
|
||||||
|
msg = 'password must be provided.'
|
||||||
|
for hasher_class in hasher_classes:
|
||||||
|
hasher = hasher_class()
|
||||||
|
with self.subTest(hasher_class.__name__):
|
||||||
|
with self.assertRaisesMessage(TypeError, msg):
|
||||||
|
hasher.encode(None, 'seasalt')
|
||||||
|
|
||||||
|
|
||||||
class BasePasswordHasherTests(SimpleTestCase):
|
class BasePasswordHasherTests(SimpleTestCase):
|
||||||
not_implemented_msg = 'subclasses of BasePasswordHasher must provide %s() method'
|
not_implemented_msg = 'subclasses of BasePasswordHasher must provide %s() method'
|
||||||
|
|
Loading…
Reference in New Issue