Fixed #29324 -- Made SECRET_KEY validation lazy (on first access).

This commit is contained in:
Florian Apolloner 2020-07-29 09:06:54 +02:00 committed by GitHub
parent 9c9a3fe118
commit 948a874425
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 26 additions and 17 deletions

View File

@ -81,6 +81,8 @@ class LazySettings(LazyObject):
# This is done here for performance reasons so the modified value is cached. # This is done here for performance reasons so the modified value is cached.
if name in {'MEDIA_URL', 'STATIC_URL'} and val is not None: if name in {'MEDIA_URL', 'STATIC_URL'} and val is not None:
val = self._add_script_prefix(val) val = self._add_script_prefix(val)
elif name == 'SECRET_KEY' and not val:
raise ImproperlyConfigured("The SECRET_KEY setting must not be empty.")
self.__dict__[name] = val self.__dict__[name] = val
return val return val
@ -184,9 +186,6 @@ class Settings:
setattr(self, setting, setting_value) setattr(self, setting, setting_value)
self._explicit_settings.add(setting) self._explicit_settings.add(setting)
if not self.SECRET_KEY:
raise ImproperlyConfigured("The SECRET_KEY setting must not be empty.")
if self.is_overridden('PASSWORD_RESET_TIMEOUT_DAYS'): if self.is_overridden('PASSWORD_RESET_TIMEOUT_DAYS'):
if self.is_overridden('PASSWORD_RESET_TIMEOUT'): if self.is_overridden('PASSWORD_RESET_TIMEOUT'):
raise ImproperlyConfigured( raise ImproperlyConfigured(

View File

@ -12,7 +12,10 @@ class PasswordResetTokenGenerator:
""" """
key_salt = "django.contrib.auth.tokens.PasswordResetTokenGenerator" key_salt = "django.contrib.auth.tokens.PasswordResetTokenGenerator"
algorithm = 'sha256' algorithm = 'sha256'
secret = settings.SECRET_KEY secret = None
def __init__(self):
self.secret = self.secret or settings.SECRET_KEY
def make_token(self, user): def make_token(self, user):
""" """

View File

@ -1,4 +1,5 @@
from django.conf import settings from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from .. import Error, Tags, Warning, register from .. import Error, Tags, Warning, register
@ -182,11 +183,15 @@ def check_ssl_redirect(app_configs, **kwargs):
@register(Tags.security, deploy=True) @register(Tags.security, deploy=True)
def check_secret_key(app_configs, **kwargs): def check_secret_key(app_configs, **kwargs):
passed_check = ( try:
getattr(settings, 'SECRET_KEY', None) and secret_key = settings.SECRET_KEY
len(set(settings.SECRET_KEY)) >= SECRET_KEY_MIN_UNIQUE_CHARACTERS and except (ImproperlyConfigured, AttributeError):
len(settings.SECRET_KEY) >= SECRET_KEY_MIN_LENGTH passed_check = False
) else:
passed_check = (
len(set(secret_key)) >= SECRET_KEY_MIN_UNIQUE_CHARACTERS and
len(secret_key) >= SECRET_KEY_MIN_LENGTH
)
return [] if passed_check else [W009] return [] if passed_check else [W009]

View File

@ -272,7 +272,13 @@ Requests and Responses
Security Security
~~~~~~~~ ~~~~~~~~
* ... * The :setting:`SECRET_KEY` setting is now checked for a valid value upon first
access, rather than when settings are first loaded. This enables running
management commands that do not rely on the ``SECRET_KEY`` without needing to
provide a value. As a consequence of this, calling
:func:`~django.conf.settings.configure` without providing a valid
``SECRET_KEY``, and then going on to access ``settings.SECRET_KEY`` will now
raise an :exc:`~django.core.exceptions.ImproperlyConfigured` exception.
Serialization Serialization
~~~~~~~~~~~~~ ~~~~~~~~~~~~~

View File

@ -289,15 +289,11 @@ class SettingsTests(SimpleTestCase):
with self.assertRaises(AttributeError): with self.assertRaises(AttributeError):
getattr(settings, 'TEST2') getattr(settings, 'TEST2')
@override_settings(SECRET_KEY='')
def test_no_secret_key(self): def test_no_secret_key(self):
settings_module = ModuleType('fake_settings_module')
sys.modules['fake_settings_module'] = settings_module
msg = 'The SECRET_KEY setting must not be empty.' msg = 'The SECRET_KEY setting must not be empty.'
try: with self.assertRaisesMessage(ImproperlyConfigured, msg):
with self.assertRaisesMessage(ImproperlyConfigured, msg): settings.SECRET_KEY
Settings('fake_settings_module')
finally:
del sys.modules['fake_settings_module']
def test_no_settings_module(self): def test_no_settings_module(self):
msg = ( msg = (