summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2021-07-22 09:42:07 +0200
committerGitHub <noreply@github.com>2021-07-22 09:42:07 +0200
commit83022d279c585be2c4173b36a92d4399e738150e (patch)
tree40a66e3bbb09f8f6d9cc895978deafcb7f43864a
parentc35b81b864ffa84751bac7d73046840f576491f9 (diff)
Refs #32508 -- Raised TypeError/ValueError instead of using "assert" in encode() methods of some password hashers.
-rw-r--r--django/contrib/auth/hashers.py15
-rw-r--r--tests/auth_tests/test_hashers.py34
2 files changed, 41 insertions, 8 deletions
diff --git a/django/contrib/auth/hashers.py b/django/contrib/auth/hashers.py
index e53e4da193..ccbac336dc 100644
--- a/django/contrib/auth/hashers.py
+++ b/django/contrib/auth/hashers.py
@@ -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)
diff --git a/tests/auth_tests/test_hashers.py b/tests/auth_tests/test_hashers.py
index 411bcea8e9..46ac4062f3 100644
--- a/tests/auth_tests/test_hashers.py
+++ b/tests/auth_tests/test_hashers.py
@@ -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'