Refs #32508 -- Raised TypeError/ValueError instead of using "assert" in encode() methods of some password hashers.

This commit is contained in:
Mariusz Felisiak 2021-07-22 09:42:07 +02:00 committed by GitHub
parent c35b81b864
commit 83022d279c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 41 additions and 8 deletions

View File

@ -209,6 +209,12 @@ class BasePasswordHasher:
"""Check if the given password is correct."""
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):
"""
Create an encoded database value.
@ -269,8 +275,7 @@ class PBKDF2PasswordHasher(BasePasswordHasher):
digest = hashlib.sha256
def encode(self, password, salt, iterations=None):
assert password is not None
assert salt and '$' not in salt
self._check_encode_args(password, salt)
iterations = iterations or self.iterations
hash = pbkdf2(password, salt, iterations, digest=self.digest)
hash = base64.b64encode(hash).decode('ascii').strip()
@ -519,8 +524,7 @@ class SHA1PasswordHasher(BasePasswordHasher):
algorithm = "sha1"
def encode(self, password, salt):
assert password is not None
assert salt and '$' not in salt
self._check_encode_args(password, salt)
hash = hashlib.sha1((salt + password).encode()).hexdigest()
return "%s$%s$%s" % (self.algorithm, salt, hash)
@ -561,8 +565,7 @@ class MD5PasswordHasher(BasePasswordHasher):
algorithm = "md5"
def encode(self, password, salt):
assert password is not None
assert salt and '$' not in salt
self._check_encode_args(password, salt)
hash = hashlib.md5((salt + password).encode()).hexdigest()
return "%s$%s$%s" % (self.algorithm, salt, hash)

View File

@ -4,8 +4,9 @@ from django.conf.global_settings import PASSWORD_HASHERS
from django.contrib.auth.hashers import (
UNUSABLE_PASSWORD_PREFIX, UNUSABLE_PASSWORD_SUFFIX_LENGTH,
BasePasswordHasher, BCryptPasswordHasher, BCryptSHA256PasswordHasher,
PBKDF2PasswordHasher, PBKDF2SHA1PasswordHasher, check_password, get_hasher,
identify_hasher, is_password_usable, make_password,
MD5PasswordHasher, PBKDF2PasswordHasher, PBKDF2SHA1PasswordHasher,
SHA1PasswordHasher, check_password, get_hasher, identify_hasher,
is_password_usable, make_password,
)
from django.test import SimpleTestCase
from django.test.utils import override_settings
@ -474,6 +475,35 @@ class TestUtilsHashPass(SimpleTestCase):
check_password('wrong_password', encoded)
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):
not_implemented_msg = 'subclasses of BasePasswordHasher must provide %s() method'