mirror of
https://github.com/django/django.git
synced 2025-08-26 03:39:11 +00:00
Fixed #36546 -- Deprecated django.utils.crypto.constant_time_compare() in favor of hmac.compare_digest().
Signed-off-by: SaJH <wogur981208@gmail.com>
This commit is contained in:
parent
3ba24c18e7
commit
0246f47888
@ -1,3 +1,4 @@
|
||||
import hmac
|
||||
import inspect
|
||||
import re
|
||||
import warnings
|
||||
@ -6,7 +7,6 @@ from django.apps import apps as django_apps
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
|
||||
from django.middleware.csrf import rotate_token
|
||||
from django.utils.crypto import constant_time_compare
|
||||
from django.utils.deprecation import RemovedInDjango61Warning
|
||||
from django.utils.module_loading import import_string
|
||||
from django.views.decorators.debug import sensitive_variables
|
||||
@ -175,7 +175,7 @@ def login(request, user, backend=None):
|
||||
if SESSION_KEY in request.session:
|
||||
if _get_user_session_key(request) != user.pk or (
|
||||
session_auth_hash
|
||||
and not constant_time_compare(
|
||||
and not hmac.compare_digest(
|
||||
request.session.get(HASH_SESSION_KEY, ""), session_auth_hash
|
||||
)
|
||||
):
|
||||
@ -217,7 +217,7 @@ async def alogin(request, user, backend=None):
|
||||
if await request.session.ahas_key(SESSION_KEY):
|
||||
if await _aget_user_session_key(request) != user.pk or (
|
||||
session_auth_hash
|
||||
and not constant_time_compare(
|
||||
and not hmac.compare_digest(
|
||||
await request.session.aget(HASH_SESSION_KEY, ""),
|
||||
session_auth_hash,
|
||||
)
|
||||
@ -323,7 +323,7 @@ def get_user(request):
|
||||
session_hash_verified = False
|
||||
else:
|
||||
session_auth_hash = user.get_session_auth_hash()
|
||||
session_hash_verified = constant_time_compare(
|
||||
session_hash_verified = hmac.compare_digest(
|
||||
session_hash, session_auth_hash
|
||||
)
|
||||
if not session_hash_verified:
|
||||
@ -331,7 +331,7 @@ def get_user(request):
|
||||
# with the fallback secrets and stop when a matching one is
|
||||
# found.
|
||||
if session_hash and any(
|
||||
constant_time_compare(session_hash, fallback_auth_hash)
|
||||
hmac.compare_digest(session_hash, fallback_auth_hash)
|
||||
for fallback_auth_hash in user.get_session_auth_fallback_hash()
|
||||
):
|
||||
request.session.cycle_key()
|
||||
@ -364,7 +364,7 @@ async def aget_user(request):
|
||||
session_hash_verified = False
|
||||
else:
|
||||
session_auth_hash = user.get_session_auth_hash()
|
||||
session_hash_verified = constant_time_compare(
|
||||
session_hash_verified = hmac.compare_digest(
|
||||
session_hash, session_auth_hash
|
||||
)
|
||||
if not session_hash_verified:
|
||||
@ -372,7 +372,7 @@ async def aget_user(request):
|
||||
# with the fallback secrets and stop when a matching one is
|
||||
# found.
|
||||
if session_hash and any(
|
||||
constant_time_compare(session_hash, fallback_auth_hash)
|
||||
hmac.compare_digest(session_hash, fallback_auth_hash)
|
||||
for fallback_auth_hash in user.get_session_auth_fallback_hash()
|
||||
):
|
||||
await request.session.acycle_key()
|
||||
|
@ -2,6 +2,7 @@ import base64
|
||||
import binascii
|
||||
import functools
|
||||
import hashlib
|
||||
import hmac
|
||||
import importlib
|
||||
import math
|
||||
import warnings
|
||||
@ -12,12 +13,7 @@ from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.signals import setting_changed
|
||||
from django.dispatch import receiver
|
||||
from django.utils.crypto import (
|
||||
RANDOM_STRING_CHARS,
|
||||
constant_time_compare,
|
||||
get_random_string,
|
||||
pbkdf2,
|
||||
)
|
||||
from django.utils.crypto import RANDOM_STRING_CHARS, 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 _
|
||||
@ -349,7 +345,7 @@ class PBKDF2PasswordHasher(BasePasswordHasher):
|
||||
def verify(self, password, encoded):
|
||||
decoded = self.decode(encoded)
|
||||
encoded_2 = self.encode(password, decoded["salt"], decoded["iterations"])
|
||||
return constant_time_compare(encoded, encoded_2)
|
||||
return hmac.compare_digest(encoded, encoded_2)
|
||||
|
||||
def safe_summary(self, encoded):
|
||||
decoded = self.decode(encoded)
|
||||
@ -533,7 +529,7 @@ class BCryptSHA256PasswordHasher(BasePasswordHasher):
|
||||
algorithm, data = encoded.split("$", 1)
|
||||
assert algorithm == self.algorithm
|
||||
encoded_2 = self.encode(password, data.encode("ascii"))
|
||||
return constant_time_compare(encoded, encoded_2)
|
||||
return hmac.compare_digest(encoded, encoded_2)
|
||||
|
||||
def safe_summary(self, encoded):
|
||||
decoded = self.decode(encoded)
|
||||
@ -628,7 +624,7 @@ class ScryptPasswordHasher(BasePasswordHasher):
|
||||
decoded["block_size"],
|
||||
decoded["parallelism"],
|
||||
)
|
||||
return constant_time_compare(encoded, encoded_2)
|
||||
return hmac.compare_digest(encoded, encoded_2)
|
||||
|
||||
def safe_summary(self, encoded):
|
||||
decoded = self.decode(encoded)
|
||||
@ -681,7 +677,7 @@ class MD5PasswordHasher(BasePasswordHasher):
|
||||
def verify(self, password, encoded):
|
||||
decoded = self.decode(encoded)
|
||||
encoded_2 = self.encode(password, decoded["salt"])
|
||||
return constant_time_compare(encoded, encoded_2)
|
||||
return hmac.compare_digest(encoded, encoded_2)
|
||||
|
||||
def safe_summary(self, encoded):
|
||||
decoded = self.decode(encoded)
|
||||
|
@ -1,7 +1,8 @@
|
||||
import hmac
|
||||
from datetime import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.crypto import constant_time_compare, salted_hmac
|
||||
from django.utils.crypto import salted_hmac
|
||||
from django.utils.http import base36_to_int, int_to_base36
|
||||
|
||||
|
||||
@ -67,7 +68,7 @@ class PasswordResetTokenGenerator:
|
||||
|
||||
# Check that the timestamp/uid has not been tampered with
|
||||
for secret in [self.secret, *self.secret_fallbacks]:
|
||||
if constant_time_compare(
|
||||
if hmac.compare_digest(
|
||||
self._make_token_with_timestamp(user, ts, secret),
|
||||
token,
|
||||
):
|
||||
|
@ -36,12 +36,13 @@ These functions make use of all of them.
|
||||
|
||||
import base64
|
||||
import datetime
|
||||
import hmac
|
||||
import json
|
||||
import time
|
||||
import zlib
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.crypto import constant_time_compare, salted_hmac
|
||||
from django.utils.crypto import salted_hmac
|
||||
from django.utils.encoding import force_bytes
|
||||
from django.utils.module_loading import import_string
|
||||
from django.utils.regex_helper import _lazy_re_compile
|
||||
@ -209,7 +210,7 @@ class Signer:
|
||||
raise BadSignature('No "%s" found in value' % self.sep)
|
||||
value, sig = signed_value.rsplit(self.sep, 1)
|
||||
for key in [self.key, *self.fallback_keys]:
|
||||
if constant_time_compare(sig, self.signature(value, key)):
|
||||
if hmac.compare_digest(sig, self.signature(value, key)):
|
||||
return value
|
||||
raise BadSignature('Signature "%s" does not match' % sig)
|
||||
|
||||
|
@ -5,6 +5,7 @@ This module provides a middleware that implements protection
|
||||
against request forgeries from other sites.
|
||||
"""
|
||||
|
||||
import hmac
|
||||
import logging
|
||||
import string
|
||||
from collections import defaultdict
|
||||
@ -15,7 +16,7 @@ from django.core.exceptions import DisallowedHost, ImproperlyConfigured
|
||||
from django.http import HttpHeaders, UnreadablePostError
|
||||
from django.urls import get_callable
|
||||
from django.utils.cache import patch_vary_headers
|
||||
from django.utils.crypto import constant_time_compare, get_random_string
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.deprecation import MiddlewareMixin
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.http import is_same_domain
|
||||
@ -154,7 +155,7 @@ def _does_token_match(request_csrf_token, csrf_secret):
|
||||
if len(request_csrf_token) == CSRF_TOKEN_LENGTH:
|
||||
request_csrf_token = _unmask_cipher_token(request_csrf_token)
|
||||
assert len(request_csrf_token) == CSRF_SECRET_LENGTH
|
||||
return constant_time_compare(request_csrf_token, csrf_secret)
|
||||
return hmac.compare_digest(request_csrf_token, csrf_secret)
|
||||
|
||||
|
||||
class RejectRequest(Exception):
|
||||
|
@ -5,8 +5,10 @@ Django's standard crypto functions and utilities.
|
||||
import hashlib
|
||||
import hmac
|
||||
import secrets
|
||||
import warnings
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.deprecation import RemovedInDjango70Warning
|
||||
from django.utils.encoding import force_bytes
|
||||
|
||||
|
||||
@ -64,7 +66,12 @@ def get_random_string(length, allowed_chars=RANDOM_STRING_CHARS):
|
||||
|
||||
def constant_time_compare(val1, val2):
|
||||
"""Return True if the two strings are equal, False otherwise."""
|
||||
return secrets.compare_digest(force_bytes(val1), force_bytes(val2))
|
||||
warnings.warn(
|
||||
"constant_time_compare() is deprecated. Use hmac.compare_digest() instead.",
|
||||
RemovedInDjango70Warning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return hmac.compare_digest(val1, val2)
|
||||
|
||||
|
||||
def pbkdf2(password, salt, iterations, dklen=0, digest=None):
|
||||
|
@ -53,6 +53,8 @@ details on these changes.
|
||||
* The ``django.core.mail.forbid_multi_line_headers()`` and
|
||||
``django.core.mail.message.sanitize_address()`` functions will be removed.
|
||||
|
||||
* The ``django.utils.crypto.constant_time_compare()`` function will be removed.
|
||||
|
||||
.. _deprecation-removed-in-6.1:
|
||||
|
||||
6.1
|
||||
|
@ -570,6 +570,9 @@ Miscellaneous
|
||||
* The undocumented ``django.core.mail.forbid_multi_line_headers()`` and
|
||||
``django.core.mail.message.sanitize_address()`` functions are deprecated.
|
||||
|
||||
* The ``django.utils.crypto.constant_time_compare()`` function is deprecated
|
||||
because it is merely an alias of :py:func:`hmac.compare_digest`.
|
||||
|
||||
Features removed in 6.0
|
||||
=======================
|
||||
|
||||
|
@ -2,15 +2,19 @@ import hashlib
|
||||
import unittest
|
||||
|
||||
from django.test import SimpleTestCase
|
||||
from django.test.utils import ignore_warnings
|
||||
from django.utils.crypto import (
|
||||
InvalidAlgorithm,
|
||||
constant_time_compare,
|
||||
pbkdf2,
|
||||
salted_hmac,
|
||||
)
|
||||
from django.utils.deprecation import RemovedInDjango70Warning
|
||||
|
||||
|
||||
class TestUtilsCryptoMisc(SimpleTestCase):
|
||||
# RemovedInDjango70Warning.
|
||||
@ignore_warnings(category=RemovedInDjango70Warning)
|
||||
def test_constant_time_compare(self):
|
||||
# It's hard to test for constant time, just test the result.
|
||||
self.assertTrue(constant_time_compare(b"spam", b"spam"))
|
||||
@ -18,6 +22,15 @@ class TestUtilsCryptoMisc(SimpleTestCase):
|
||||
self.assertTrue(constant_time_compare("spam", "spam"))
|
||||
self.assertFalse(constant_time_compare("spam", "eggs"))
|
||||
|
||||
def test_constant_time_compare_deprecated(self):
|
||||
msg = (
|
||||
"constant_time_compare() is deprecated. "
|
||||
"Use hmac.compare_digest() instead."
|
||||
)
|
||||
with self.assertWarnsMessage(RemovedInDjango70Warning, msg) as ctx:
|
||||
constant_time_compare(b"spam", b"spam")
|
||||
self.assertEqual(ctx.filename, __file__)
|
||||
|
||||
def test_salted_hmac(self):
|
||||
tests = [
|
||||
((b"salt", b"value"), {}, "b51a2e619c43b1ca4f91d15c57455521d71d61eb"),
|
||||
|
Loading…
x
Reference in New Issue
Block a user