1
0
mirror of https://github.com/django/django.git synced 2025-06-05 03:29:12 +00:00

Refs #31842 -- Removed DEFAULT_HASHING_ALGORITHM transitional setting.

Per deprecation timeline.
This commit is contained in:
Mariusz Felisiak 2021-01-14 10:27:04 +01:00
parent e7208f13c0
commit 0aa6a602b2
15 changed files with 16 additions and 177 deletions

View File

@ -9,22 +9,14 @@ for a list of all possible variables.
import importlib import importlib
import os import os
import time import time
import warnings
from pathlib import Path from pathlib import Path
from django.conf import global_settings from django.conf import global_settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.utils.deprecation import RemovedInDjango40Warning
from django.utils.functional import LazyObject, empty from django.utils.functional import LazyObject, empty
ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE" ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"
DEFAULT_HASHING_ALGORITHM_DEPRECATED_MSG = (
'The DEFAULT_HASHING_ALGORITHM transitional setting is deprecated. '
'Support for it and tokens, cookies, sessions, and signatures that use '
'SHA-1 hashing algorithm will be removed in Django 4.0.'
)
class SettingsReference(str): class SettingsReference(str):
""" """
@ -164,9 +156,6 @@ class Settings:
setattr(self, setting, setting_value) setattr(self, setting, setting_value)
self._explicit_settings.add(setting) self._explicit_settings.add(setting)
if self.is_overridden('DEFAULT_HASHING_ALGORITHM'):
warnings.warn(DEFAULT_HASHING_ALGORITHM_DEPRECATED_MSG, RemovedInDjango40Warning)
if hasattr(time, 'tzset') and self.TIME_ZONE: if hasattr(time, 'tzset') and self.TIME_ZONE:
# When we can, attempt to validate the timezone. If we can't find # When we can, attempt to validate the timezone. If we can't find
# this file, no check happens and it's harmless. # this file, no check happens and it's harmless.
@ -210,8 +199,6 @@ class UserSettingsHolder:
def __setattr__(self, name, value): def __setattr__(self, name, value):
self._deleted.discard(name) self._deleted.discard(name)
if name == 'DEFAULT_HASHING_ALGORITHM':
warnings.warn(DEFAULT_HASHING_ALGORITHM_DEPRECATED_MSG, RemovedInDjango40Warning)
super().__setattr__(name, value) super().__setattr__(name, value)
def __delattr__(self, name): def __delattr__(self, name):

View File

@ -439,12 +439,6 @@ WSGI_APPLICATION = None
# you may be opening yourself up to a security risk. # you may be opening yourself up to a security risk.
SECURE_PROXY_SSL_HEADER = None SECURE_PROXY_SSL_HEADER = None
# Default hashing algorithm to use for encoding cookies, password reset tokens
# in the admin site, user sessions, and signatures. It's a transitional setting
# helpful in migrating multiple instance of the same project to Django 3.1+.
# Algorithm must be 'sha1' or 'sha256'.
DEFAULT_HASHING_ALGORITHM = 'sha256'
############## ##############
# MIDDLEWARE # # MIDDLEWARE #
############## ##############

View File

@ -4,7 +4,6 @@ not in INSTALLED_APPS.
""" """
import unicodedata import unicodedata
from django.conf import settings
from django.contrib.auth import password_validation from django.contrib.auth import password_validation
from django.contrib.auth.hashers import ( from django.contrib.auth.hashers import (
check_password, is_password_usable, make_password, check_password, is_password_usable, make_password,
@ -129,10 +128,7 @@ class AbstractBaseUser(models.Model):
return salted_hmac( return salted_hmac(
key_salt, key_salt,
self.password, self.password,
# RemovedInDjango40Warning: when the deprecation ends, replace algorithm='sha256',
# with:
# algorithm='sha256',
algorithm=settings.DEFAULT_HASHING_ALGORITHM,
).hexdigest() ).hexdigest()
@classmethod @classmethod

View File

@ -16,9 +16,7 @@ class PasswordResetTokenGenerator:
def __init__(self): def __init__(self):
self.secret = self.secret or settings.SECRET_KEY self.secret = self.secret or settings.SECRET_KEY
# RemovedInDjango40Warning: when the deprecation ends, replace with: self.algorithm = self.algorithm or 'sha256'
# self.algorithm = self.algorithm or 'sha256'
self.algorithm = self.algorithm or settings.DEFAULT_HASHING_ALGORITHM
def make_token(self, user): def make_token(self, user):
""" """

View File

@ -119,11 +119,6 @@ E023 = Error(
id='security.E023', id='security.E023',
) )
E100 = Error(
"DEFAULT_HASHING_ALGORITHM must be 'sha1' or 'sha256'.",
id='security.E100',
)
def _security_middleware(): def _security_middleware():
return 'django.middleware.security.SecurityMiddleware' in settings.MIDDLEWARE return 'django.middleware.security.SecurityMiddleware' in settings.MIDDLEWARE
@ -237,11 +232,3 @@ def check_referrer_policy(app_configs, **kwargs):
if not values <= REFERRER_POLICY_VALUES: if not values <= REFERRER_POLICY_VALUES:
return [E023] return [E023]
return [] return []
# RemovedInDjango40Warning
@register(Tags.security)
def check_default_hashing_algorithm(app_configs, **kwargs):
if settings.DEFAULT_HASHING_ALGORITHM not in {'sha1', 'sha256'}:
return [E100]
return []

View File

@ -129,9 +129,7 @@ class Signer:
'only A-z0-9-_=)' % sep, 'only A-z0-9-_=)' % sep,
) )
self.salt = salt or '%s.%s' % (self.__class__.__module__, self.__class__.__name__) self.salt = salt or '%s.%s' % (self.__class__.__module__, self.__class__.__name__)
# RemovedInDjango40Warning: when the deprecation ends, replace with: self.algorithm = algorithm or 'sha256'
# self.algorithm = algorithm or 'sha256'
self.algorithm = algorithm or settings.DEFAULT_HASHING_ALGORITHM
def signature(self, value): def signature(self, value):
return base64_hmac(self.salt + 'signer', value, self.key, algorithm=self.algorithm) return base64_hmac(self.salt + 'signer', value, self.key, algorithm=self.algorithm)

View File

@ -494,8 +494,8 @@ The following checks are run if you use the :option:`check --deploy` option:
The following checks verify that your security-related settings are correctly The following checks verify that your security-related settings are correctly
configured: configured:
* **security.E100**: :setting:`DEFAULT_HASHING_ALGORITHM` must be ``'sha1'`` or * **security.E100**: ``DEFAULT_HASHING_ALGORITHM`` must be ``'sha1'`` or
``'sha256'``. ``'sha256'``. *This check appeared in Django 3.1 and 3.2*.
* **security.E101**: The CSRF failure view ``'path.to.view'`` does not take the * **security.E101**: The CSRF failure view ``'path.to.view'`` does not take the
correct number of arguments. correct number of arguments.
* **security.E102**: The CSRF failure view ``'path.to.view'`` could not be * **security.E102**: The CSRF failure view ``'path.to.view'`` could not be

View File

@ -1291,25 +1291,6 @@ Default email address to use for various automated correspondence from the
site manager(s). This doesn't include error messages sent to :setting:`ADMINS` site manager(s). This doesn't include error messages sent to :setting:`ADMINS`
and :setting:`MANAGERS`; for that, see :setting:`SERVER_EMAIL`. and :setting:`MANAGERS`; for that, see :setting:`SERVER_EMAIL`.
.. setting:: DEFAULT_HASHING_ALGORITHM
``DEFAULT_HASHING_ALGORITHM``
-----------------------------
Default: ``'sha256'``
Default hashing algorithm to use for encoding cookies, password reset tokens in
the admin site, user sessions, and signatures created by
:class:`django.core.signing.Signer` and :meth:`django.core.signing.dumps`.
Algorithm must be ``'sha1'`` or ``'sha256'``. See
:ref:`release notes <default-hashing-algorithm-usage>` for usage details.
.. deprecated:: 3.1
This transitional setting is deprecated. Support for it and tokens,
cookies, sessions, and signatures that use SHA-1 hashing algorithm will be
removed in Django 4.0.
.. setting:: DEFAULT_INDEX_TABLESPACE .. setting:: DEFAULT_INDEX_TABLESPACE
``DEFAULT_INDEX_TABLESPACE`` ``DEFAULT_INDEX_TABLESPACE``

View File

@ -101,17 +101,17 @@ of this release <deprecated-jsonfield>`.
``DEFAULT_HASHING_ALGORITHM`` settings ``DEFAULT_HASHING_ALGORITHM`` settings
-------------------------------------- --------------------------------------
The new :setting:`DEFAULT_HASHING_ALGORITHM` transitional setting allows The new ``DEFAULT_HASHING_ALGORITHM`` transitional setting allows specifying
specifying the default hashing algorithm to use for encoding cookies, password the default hashing algorithm to use for encoding cookies, password reset
reset tokens in the admin site, user sessions, and signatures created by tokens in the admin site, user sessions, and signatures created by
:class:`django.core.signing.Signer` and :meth:`django.core.signing.dumps`. :class:`django.core.signing.Signer` and :meth:`django.core.signing.dumps`.
Support for SHA-256 was added in Django 3.1. If you are upgrading multiple Support for SHA-256 was added in Django 3.1. If you are upgrading multiple
instances of the same project to Django 3.1, you should set instances of the same project to Django 3.1, you should set
:setting:`DEFAULT_HASHING_ALGORITHM` to ``'sha1'`` during the transition, in ``DEFAULT_HASHING_ALGORITHM`` to ``'sha1'`` during the transition, in order to
order to allow compatibility with the older versions of Django. Note that this allow compatibility with the older versions of Django. Note that this requires
requires Django 3.1.1+. Once the transition to 3.1 is complete you can stop Django 3.1.1+. Once the transition to 3.1 is complete you can stop overriding
overriding :setting:`DEFAULT_HASHING_ALGORITHM`. ``DEFAULT_HASHING_ALGORITHM``.
This setting is deprecated as of this release, because support for tokens, This setting is deprecated as of this release, because support for tokens,
cookies, sessions, and signatures that use SHA-1 algorithm will be removed in cookies, sessions, and signatures that use SHA-1 algorithm will be removed in

View File

@ -323,3 +323,5 @@ to remove usage of these features.
* ``django.contrib.postgres.forms.JSONField`` is removed. * ``django.contrib.postgres.forms.JSONField`` is removed.
* The ``{% ifequal %}`` and ``{% ifnotequal %}`` template tags are removed. * The ``{% ifequal %}`` and ``{% ifnotequal %}`` template tags are removed.
* The ``DEFAULT_HASHING_ALGORITHM`` transitional setting is removed.

View File

@ -1,10 +1,7 @@
from django.contrib.auth import HASH_SESSION_KEY
from django.contrib.auth.middleware import AuthenticationMiddleware from django.contrib.auth.middleware import AuthenticationMiddleware
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.test import TestCase, override_settings from django.test import TestCase
from django.test.utils import ignore_warnings
from django.utils.deprecation import RemovedInDjango40Warning
class TestAuthenticationMiddleware(TestCase): class TestAuthenticationMiddleware(TestCase):
@ -24,12 +21,6 @@ class TestAuthenticationMiddleware(TestCase):
self.assertIsNotNone(self.request.user) self.assertIsNotNone(self.request.user)
self.assertFalse(self.request.user.is_anonymous) self.assertFalse(self.request.user.is_anonymous)
@ignore_warnings(category=RemovedInDjango40Warning)
def test_session_default_hashing_algorithm(self):
hash_session = self.client.session[HASH_SESSION_KEY]
with override_settings(DEFAULT_HASHING_ALGORITHM='sha1'):
self.assertNotEqual(hash_session, self.user.get_session_auth_hash())
def test_changed_password_invalidates_session(self): def test_changed_password_invalidates_session(self):
# After password change, user should be anonymous # After password change, user should be anonymous
self.user.set_password('new_password') self.user.set_password('new_password')

View File

@ -4,8 +4,6 @@ from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth.tokens import PasswordResetTokenGenerator from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.test import TestCase from django.test import TestCase
from django.test.utils import ignore_warnings
from django.utils.deprecation import RemovedInDjango40Warning
from .models import CustomEmailField from .models import CustomEmailField
@ -113,12 +111,3 @@ class TokenGeneratorTest(TestCase):
# Tokens created with a different secret don't validate. # Tokens created with a different secret don't validate.
self.assertIs(p0.check_token(user, tk1), False) self.assertIs(p0.check_token(user, tk1), False)
self.assertIs(p1.check_token(user, tk0), False) self.assertIs(p1.check_token(user, tk0), False)
@ignore_warnings(category=RemovedInDjango40Warning)
def test_token_default_hashing_algorithm(self):
user = User.objects.create_user('tokentestuser', 'test2@example.com', 'testpw')
with self.settings(DEFAULT_HASHING_ALGORITHM='sha1'):
generator = PasswordResetTokenGenerator()
self.assertEqual(generator.algorithm, 'sha1')
token = generator.make_token(user)
self.assertIs(generator.check_token(user, token), True)

View File

@ -1,55 +0,0 @@
import sys
from types import ModuleType
from django.conf import (
DEFAULT_HASHING_ALGORITHM_DEPRECATED_MSG, Settings, settings,
)
from django.core.checks.security import base as security_base
from django.test import TestCase, ignore_warnings
from django.utils.deprecation import RemovedInDjango40Warning
class DefaultHashingAlgorithmDeprecationTests(TestCase):
msg = DEFAULT_HASHING_ALGORITHM_DEPRECATED_MSG
def test_override_settings_warning(self):
with self.assertRaisesMessage(RemovedInDjango40Warning, self.msg):
with self.settings(DEFAULT_HASHING_ALGORITHM='sha1'):
pass
def test_settings_init_warning(self):
settings_module = ModuleType('fake_settings_module')
settings_module.SECRET_KEY = 'foo'
settings_module.DEFAULT_HASHING_ALGORITHM = 'sha1'
sys.modules['fake_settings_module'] = settings_module
try:
with self.assertRaisesMessage(RemovedInDjango40Warning, self.msg):
Settings('fake_settings_module')
finally:
del sys.modules['fake_settings_module']
def test_access(self):
# Warning is not raised on access.
self.assertEqual(settings.DEFAULT_HASHING_ALGORITHM, 'sha256')
@ignore_warnings(category=RemovedInDjango40Warning)
def test_system_check_invalid_value(self):
tests = [
None,
256,
'invalid',
'md5',
'sha512',
]
for value in tests:
with self.subTest(value=value), self.settings(DEFAULT_HASHING_ALGORITHM=value):
self.assertEqual(
security_base.check_default_hashing_algorithm(None),
[security_base.E100],
)
@ignore_warnings(category=RemovedInDjango40Warning)
def test_system_check_valid_value(self):
for value in ['sha1', 'sha256']:
with self.subTest(value=value), self.settings(DEFAULT_HASHING_ALGORITHM=value):
self.assertEqual(security_base.check_default_hashing_algorithm(None), [])

View File

@ -9,9 +9,7 @@ from django.contrib.messages.storage.cookie import (
) )
from django.core.signing import get_cookie_signer from django.core.signing import get_cookie_signer
from django.test import SimpleTestCase, override_settings from django.test import SimpleTestCase, override_settings
from django.test.utils import ignore_warnings
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
from django.utils.deprecation import RemovedInDjango40Warning
from django.utils.safestring import SafeData, mark_safe from django.utils.safestring import SafeData, mark_safe
from .base import BaseTests from .base import BaseTests
@ -193,14 +191,3 @@ class CookieTests(BaseTests, SimpleTestCase):
encoded_messages = signer.sign(value) encoded_messages = signer.sign(value)
decoded_messages = storage._decode(encoded_messages) decoded_messages = storage._decode(encoded_messages)
self.assertEqual(messages, decoded_messages) self.assertEqual(messages, decoded_messages)
@ignore_warnings(category=RemovedInDjango40Warning)
def test_default_hashing_algorithm(self):
messages = Message(constants.DEBUG, ['this', 'that'])
with self.settings(DEFAULT_HASHING_ALGORITHM='sha1'):
storage = self.get_storage()
encoded = storage._encode(messages)
decoded = storage._decode(encoded)
self.assertEqual(decoded, messages)
storage_default = self.get_storage()
self.assertNotEqual(encoded, storage_default._encode(messages))

View File

@ -2,9 +2,8 @@ import datetime
from django.core import signing from django.core import signing
from django.test import SimpleTestCase from django.test import SimpleTestCase
from django.test.utils import freeze_time, ignore_warnings from django.test.utils import freeze_time
from django.utils.crypto import InvalidAlgorithm from django.utils.crypto import InvalidAlgorithm
from django.utils.deprecation import RemovedInDjango40Warning
class TestSigner(SimpleTestCase): class TestSigner(SimpleTestCase):
@ -53,14 +52,6 @@ class TestSigner(SimpleTestCase):
'VzO9_jVu7R-VkqknHYNvw', 'VzO9_jVu7R-VkqknHYNvw',
) )
@ignore_warnings(category=RemovedInDjango40Warning)
def test_default_hashing_algorithm(self):
signer = signing.Signer('predictable-secret', algorithm='sha1')
signature_sha1 = signer.signature('hello')
with self.settings(DEFAULT_HASHING_ALGORITHM='sha1'):
signer = signing.Signer('predictable-secret')
self.assertEqual(signer.signature('hello'), signature_sha1)
def test_invalid_algorithm(self): def test_invalid_algorithm(self):
signer = signing.Signer('predictable-secret', algorithm='whatever') signer = signing.Signer('predictable-secret', algorithm='whatever')
msg = "'whatever' is not an algorithm accepted by the hashlib module." msg = "'whatever' is not an algorithm accepted by the hashlib module."
@ -143,13 +134,6 @@ class TestSigner(SimpleTestCase):
self.assertNotEqual(o, signing.dumps(o, compress=True)) self.assertNotEqual(o, signing.dumps(o, compress=True))
self.assertEqual(o, signing.loads(signing.dumps(o, compress=True))) self.assertEqual(o, signing.loads(signing.dumps(o, compress=True)))
@ignore_warnings(category=RemovedInDjango40Warning)
def test_dumps_loads_default_hashing_algorithm_sha1(self):
value = 'a string \u2020'
with self.settings(DEFAULT_HASHING_ALGORITHM='sha1'):
signed = signing.dumps(value)
self.assertEqual(signing.loads(signed), value)
def test_decode_detects_tampering(self): def test_decode_detects_tampering(self):
"loads should raise exception for tampered objects" "loads should raise exception for tampered objects"
transforms = ( transforms = (