diff --git a/django/conf/__init__.py b/django/conf/__init__.py index 8ebc3c70ce..03bf923bb2 100644 --- a/django/conf/__init__.py +++ b/django/conf/__init__.py @@ -9,22 +9,14 @@ for a list of all possible variables. import importlib import os import time -import warnings from pathlib import Path from django.conf import global_settings from django.core.exceptions import ImproperlyConfigured -from django.utils.deprecation import RemovedInDjango40Warning from django.utils.functional import LazyObject, empty 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): """ @@ -164,9 +156,6 @@ class Settings: setattr(self, setting, setting_value) 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: # When we can, attempt to validate the timezone. If we can't find # this file, no check happens and it's harmless. @@ -210,8 +199,6 @@ class UserSettingsHolder: def __setattr__(self, name, value): self._deleted.discard(name) - if name == 'DEFAULT_HASHING_ALGORITHM': - warnings.warn(DEFAULT_HASHING_ALGORITHM_DEPRECATED_MSG, RemovedInDjango40Warning) super().__setattr__(name, value) def __delattr__(self, name): diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 44f8275a24..8108f8f762 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -439,12 +439,6 @@ WSGI_APPLICATION = None # you may be opening yourself up to a security risk. 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 # ############## diff --git a/django/contrib/auth/base_user.py b/django/contrib/auth/base_user.py index 26145a7e50..945a7a3d8a 100644 --- a/django/contrib/auth/base_user.py +++ b/django/contrib/auth/base_user.py @@ -4,7 +4,6 @@ not in INSTALLED_APPS. """ import unicodedata -from django.conf import settings from django.contrib.auth import password_validation from django.contrib.auth.hashers import ( check_password, is_password_usable, make_password, @@ -129,10 +128,7 @@ class AbstractBaseUser(models.Model): return salted_hmac( key_salt, self.password, - # RemovedInDjango40Warning: when the deprecation ends, replace - # with: - # algorithm='sha256', - algorithm=settings.DEFAULT_HASHING_ALGORITHM, + algorithm='sha256', ).hexdigest() @classmethod diff --git a/django/contrib/auth/tokens.py b/django/contrib/auth/tokens.py index c5c23c9b72..55d80e3153 100644 --- a/django/contrib/auth/tokens.py +++ b/django/contrib/auth/tokens.py @@ -16,9 +16,7 @@ class PasswordResetTokenGenerator: def __init__(self): 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 settings.DEFAULT_HASHING_ALGORITHM + self.algorithm = self.algorithm or 'sha256' def make_token(self, user): """ diff --git a/django/core/checks/security/base.py b/django/core/checks/security/base.py index 8cf3d1d61e..0de9532def 100644 --- a/django/core/checks/security/base.py +++ b/django/core/checks/security/base.py @@ -119,11 +119,6 @@ E023 = Error( id='security.E023', ) -E100 = Error( - "DEFAULT_HASHING_ALGORITHM must be 'sha1' or 'sha256'.", - id='security.E100', -) - def _security_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: return [E023] 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 [] diff --git a/django/core/signing.py b/django/core/signing.py index 804b6e773c..0a566ff47d 100644 --- a/django/core/signing.py +++ b/django/core/signing.py @@ -129,9 +129,7 @@ class Signer: 'only A-z0-9-_=)' % sep, ) 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 settings.DEFAULT_HASHING_ALGORITHM + self.algorithm = algorithm or 'sha256' def signature(self, value): return base64_hmac(self.salt + 'signer', value, self.key, algorithm=self.algorithm) diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt index aae23d1d20..1025549370 100644 --- a/docs/ref/checks.txt +++ b/docs/ref/checks.txt @@ -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 configured: -* **security.E100**: :setting:`DEFAULT_HASHING_ALGORITHM` must be ``'sha1'`` or - ``'sha256'``. +* **security.E100**: ``DEFAULT_HASHING_ALGORITHM`` must be ``'sha1'`` or + ``'sha256'``. *This check appeared in Django 3.1 and 3.2*. * **security.E101**: The CSRF failure view ``'path.to.view'`` does not take the correct number of arguments. * **security.E102**: The CSRF failure view ``'path.to.view'`` could not be diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 796045aebd..09eeb1e52f 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -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` 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 ` 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 ``DEFAULT_INDEX_TABLESPACE`` diff --git a/docs/releases/3.1.txt b/docs/releases/3.1.txt index 730d0b682a..e6c040f22e 100644 --- a/docs/releases/3.1.txt +++ b/docs/releases/3.1.txt @@ -101,17 +101,17 @@ of this release `. ``DEFAULT_HASHING_ALGORITHM`` settings -------------------------------------- -The new :setting:`DEFAULT_HASHING_ALGORITHM` transitional setting allows -specifying the default hashing algorithm to use for encoding cookies, password -reset tokens in the admin site, user sessions, and signatures created by +The new ``DEFAULT_HASHING_ALGORITHM`` transitional setting allows specifying +the 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`. 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 -:setting:`DEFAULT_HASHING_ALGORITHM` to ``'sha1'`` during the transition, in -order to allow compatibility with the older versions of Django. Note that this -requires Django 3.1.1+. Once the transition to 3.1 is complete you can stop -overriding :setting:`DEFAULT_HASHING_ALGORITHM`. +``DEFAULT_HASHING_ALGORITHM`` to ``'sha1'`` during the transition, in order to +allow compatibility with the older versions of Django. Note that this requires +Django 3.1.1+. Once the transition to 3.1 is complete you can stop overriding +``DEFAULT_HASHING_ALGORITHM``. 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 diff --git a/docs/releases/4.0.txt b/docs/releases/4.0.txt index 0dd1a3d91a..c8106e2d39 100644 --- a/docs/releases/4.0.txt +++ b/docs/releases/4.0.txt @@ -323,3 +323,5 @@ to remove usage of these features. * ``django.contrib.postgres.forms.JSONField`` is removed. * The ``{% ifequal %}`` and ``{% ifnotequal %}`` template tags are removed. + +* The ``DEFAULT_HASHING_ALGORITHM`` transitional setting is removed. diff --git a/tests/auth_tests/test_middleware.py b/tests/auth_tests/test_middleware.py index c6e0a7cc8d..565d0f7692 100644 --- a/tests/auth_tests/test_middleware.py +++ b/tests/auth_tests/test_middleware.py @@ -1,10 +1,7 @@ -from django.contrib.auth import HASH_SESSION_KEY from django.contrib.auth.middleware import AuthenticationMiddleware from django.contrib.auth.models import User from django.http import HttpRequest, HttpResponse -from django.test import TestCase, override_settings -from django.test.utils import ignore_warnings -from django.utils.deprecation import RemovedInDjango40Warning +from django.test import TestCase class TestAuthenticationMiddleware(TestCase): @@ -24,12 +21,6 @@ class TestAuthenticationMiddleware(TestCase): self.assertIsNotNone(self.request.user) 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): # After password change, user should be anonymous self.user.set_password('new_password') diff --git a/tests/auth_tests/test_tokens.py b/tests/auth_tests/test_tokens.py index 1d98dcb966..5b48eb9785 100644 --- a/tests/auth_tests/test_tokens.py +++ b/tests/auth_tests/test_tokens.py @@ -4,8 +4,6 @@ from django.conf import settings from django.contrib.auth.models import User from django.contrib.auth.tokens import PasswordResetTokenGenerator from django.test import TestCase -from django.test.utils import ignore_warnings -from django.utils.deprecation import RemovedInDjango40Warning from .models import CustomEmailField @@ -113,12 +111,3 @@ class TokenGeneratorTest(TestCase): # Tokens created with a different secret don't validate. self.assertIs(p0.check_token(user, tk1), 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) diff --git a/tests/deprecation/test_default_hashing_algorithm.py b/tests/deprecation/test_default_hashing_algorithm.py deleted file mode 100644 index 078449ce4e..0000000000 --- a/tests/deprecation/test_default_hashing_algorithm.py +++ /dev/null @@ -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), []) diff --git a/tests/messages_tests/test_cookie.py b/tests/messages_tests/test_cookie.py index 8df75fa973..4bc6f119af 100644 --- a/tests/messages_tests/test_cookie.py +++ b/tests/messages_tests/test_cookie.py @@ -9,9 +9,7 @@ from django.contrib.messages.storage.cookie import ( ) from django.core.signing import get_cookie_signer 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.deprecation import RemovedInDjango40Warning from django.utils.safestring import SafeData, mark_safe from .base import BaseTests @@ -193,14 +191,3 @@ class CookieTests(BaseTests, SimpleTestCase): encoded_messages = signer.sign(value) decoded_messages = storage._decode(encoded_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)) diff --git a/tests/signing/tests.py b/tests/signing/tests.py index f4c20b9aff..4b9cec7d25 100644 --- a/tests/signing/tests.py +++ b/tests/signing/tests.py @@ -2,9 +2,8 @@ import datetime from django.core import signing 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.deprecation import RemovedInDjango40Warning class TestSigner(SimpleTestCase): @@ -53,14 +52,6 @@ class TestSigner(SimpleTestCase): '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): signer = signing.Signer('predictable-secret', algorithm='whatever') 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.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): "loads should raise exception for tampered objects" transforms = (