summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorRoel Delos Reyes <roel.delosreyes@finqle.com>2025-07-09 00:14:00 +0800
committerSarah Boyce <42296566+sarahboyce@users.noreply.github.com>2025-07-22 12:15:10 +0200
commit78fac1b0473ed8960ecd2a30aca4fa8420d150b8 (patch)
treea9bfd953c57faf43a93c126700c2c286d3e90dcd /django
parente709301000edddc48ebfe9535e2e2177d3bcedb1 (diff)
Fixed #36226 -- Accepted str or bytes for password and salt in password hashers.
Co-authored-by: Screamadelica <1621456391@sjtu.edu.cn>
Diffstat (limited to 'django')
-rw-r--r--django/contrib/auth/hashers.py20
1 files changed, 13 insertions, 7 deletions
diff --git a/django/contrib/auth/hashers.py b/django/contrib/auth/hashers.py
index 3c88692845..aa183c3904 100644
--- a/django/contrib/auth/hashers.py
+++ b/django/contrib/auth/hashers.py
@@ -16,6 +16,7 @@ from django.utils.crypto import (
get_random_string,
pbkdf2,
)
+from django.utils.encoding import force_bytes, force_str
from django.utils.module_loading import import_string
from django.utils.translation import gettext_noop as _
@@ -252,7 +253,7 @@ class BasePasswordHasher:
def _check_encode_args(self, password, salt):
if password is None:
raise TypeError("password must be provided.")
- if not salt or "$" in salt:
+ if not salt or "$" in force_str(salt): # salt can be str or bytes.
raise ValueError("salt must be provided and cannot contain $.")
def encode(self, password, salt):
@@ -324,6 +325,8 @@ class PBKDF2PasswordHasher(BasePasswordHasher):
def encode(self, password, salt, iterations=None):
self._check_encode_args(password, salt)
iterations = iterations or self.iterations
+ password = force_str(password)
+ salt = force_str(salt)
hash = pbkdf2(password, salt, iterations, digest=self.digest)
hash = base64.b64encode(hash).decode("ascii").strip()
return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash)
@@ -396,8 +399,8 @@ class Argon2PasswordHasher(BasePasswordHasher):
argon2 = self._load_library()
params = self.params()
data = argon2.low_level.hash_secret(
- password.encode(),
- salt.encode(),
+ force_bytes(password),
+ force_bytes(salt),
time_cost=params.time_cost,
memory_cost=params.memory_cost,
parallelism=params.parallelism,
@@ -499,7 +502,8 @@ class BCryptSHA256PasswordHasher(BasePasswordHasher):
def encode(self, password, salt):
bcrypt = self._load_library()
- password = password.encode()
+ password = force_bytes(password)
+ salt = force_bytes(salt)
# Hash the password prior to using bcrypt to prevent password
# truncation as described in #20138.
if self.digest is not None:
@@ -585,8 +589,8 @@ class ScryptPasswordHasher(BasePasswordHasher):
r = r or self.block_size
p = p or self.parallelism
hash_ = hashlib.scrypt(
- password.encode(),
- salt=salt.encode(),
+ password=force_bytes(password),
+ salt=force_bytes(salt),
n=n,
r=r,
p=p,
@@ -594,7 +598,7 @@ class ScryptPasswordHasher(BasePasswordHasher):
dklen=64,
)
hash_ = base64.b64encode(hash_).decode("ascii").strip()
- return "%s$%d$%s$%d$%d$%s" % (self.algorithm, n, salt, r, p, hash_)
+ return "%s$%d$%s$%d$%d$%s" % (self.algorithm, n, force_str(salt), r, p, hash_)
def decode(self, encoded):
algorithm, work_factor, salt, block_size, parallelism, hash_ = encoded.split(
@@ -655,6 +659,8 @@ class MD5PasswordHasher(BasePasswordHasher):
def encode(self, password, salt):
self._check_encode_args(password, salt)
+ password = force_str(password)
+ salt = force_str(salt)
hash = hashlib.md5((salt + password).encode()).hexdigest()
return "%s$%s$%s" % (self.algorithm, salt, hash)