Browse Source

Refs #31842 -- Removed DEFAULT_HASHING_ALGORITHM transitional setting.

Per deprecation timeline.
Mariusz Felisiak 4 years ago
parent
commit
0aa6a602b2

+ 0 - 13
django/conf/__init__.py

@@ -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):

+ 0 - 6
django/conf/global_settings.py

@@ -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 #
 ##############
 ##############

+ 1 - 5
django/contrib/auth/base_user.py

@@ -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
-            # with:
-            # algorithm='sha256',
-            algorithm=settings.DEFAULT_HASHING_ALGORITHM,
+            algorithm='sha256',
         ).hexdigest()
         ).hexdigest()
 
 
     @classmethod
     @classmethod

+ 1 - 3
django/contrib/auth/tokens.py

@@ -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 settings.DEFAULT_HASHING_ALGORITHM
+        self.algorithm = self.algorithm or 'sha256'
 
 
     def make_token(self, user):
     def make_token(self, user):
         """
         """

+ 0 - 13
django/core/checks/security/base.py

@@ -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 []

+ 1 - 3
django/core/signing.py

@@ -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 settings.DEFAULT_HASHING_ALGORITHM
+        self.algorithm = algorithm or 'sha256'
 
 
     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)

+ 2 - 2
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
 The following checks verify that your security-related settings are correctly
 configured:
 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
 * **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

+ 0 - 19
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`
 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``

+ 7 - 7
docs/releases/3.1.txt

@@ -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
-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`.
 :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
-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,
 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

+ 2 - 0
docs/releases/4.0.txt

@@ -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.

+ 1 - 10
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.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.utils import ignore_warnings
-from django.utils.deprecation import RemovedInDjango40Warning
+from django.test import TestCase
 
 
 
 
 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')

+ 0 - 11
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.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)

+ 0 - 55
tests/deprecation/test_default_hashing_algorithm.py

@@ -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), [])

+ 0 - 13
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.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))

+ 1 - 17
tests/signing/tests.py

@@ -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 = (