Browse Source

Fixed #31757 -- Adjusted system check for SECRET_KEY to warn about autogenerated default keys.

Thanks Nick Pope, René Fleschenberg, and Carlton Gibson for reviews.
Artem Kosenko 4 năm trước cách đây
mục cha
commit
b7f500396e

+ 9 - 5
django/core/checks/security/base.py

@@ -9,6 +9,7 @@ REFERRER_POLICY_VALUES = {
     'strict-origin-when-cross-origin', 'unsafe-url',
 }
 
+SECRET_KEY_INSECURE_PREFIX = 'django-insecure-'
 SECRET_KEY_MIN_LENGTH = 50
 SECRET_KEY_MIN_UNIQUE_CHARACTERS = 5
 
@@ -68,12 +69,14 @@ W008 = Warning(
 )
 
 W009 = Warning(
-    "Your SECRET_KEY has less than %(min_length)s characters or less than "
-    "%(min_unique_chars)s unique characters. Please generate a long and random "
-    "SECRET_KEY, otherwise many of Django's security-critical features will be "
-    "vulnerable to attack." % {
+    "Your SECRET_KEY has less than %(min_length)s characters, less than "
+    "%(min_unique_chars)s unique characters, or it's prefixed with "
+    "'%(insecure_prefix)s' indicating that it was generated automatically by "
+    "Django. Please generate a long and random SECRET_KEY, otherwise many of "
+    "Django's security-critical features will be vulnerable to attack." % {
         'min_length': SECRET_KEY_MIN_LENGTH,
         'min_unique_chars': SECRET_KEY_MIN_UNIQUE_CHARACTERS,
+        'insecure_prefix': SECRET_KEY_INSECURE_PREFIX,
     },
     id='security.W009',
 )
@@ -195,7 +198,8 @@ def check_secret_key(app_configs, **kwargs):
     else:
         passed_check = (
             len(set(secret_key)) >= SECRET_KEY_MIN_UNIQUE_CHARACTERS and
-            len(secret_key) >= SECRET_KEY_MIN_LENGTH
+            len(secret_key) >= SECRET_KEY_MIN_LENGTH and
+            not secret_key.startswith(SECRET_KEY_INSECURE_PREFIX)
         )
     return [] if passed_check else [W009]
 

+ 2 - 1
django/core/management/commands/startproject.py

@@ -1,3 +1,4 @@
+from django.core.checks.security.base import SECRET_KEY_INSECURE_PREFIX
 from django.core.management.templates import TemplateCommand
 
 from ..utils import get_random_secret_key
@@ -15,6 +16,6 @@ class Command(TemplateCommand):
         target = options.pop('directory')
 
         # Create a random SECRET_KEY to put it in the main settings.
-        options['secret_key'] = get_random_secret_key()
+        options['secret_key'] = SECRET_KEY_INSECURE_PREFIX + get_random_secret_key()
 
         super().handle('project', project_name, target, **options)

+ 5 - 4
docs/ref/checks.txt

@@ -437,10 +437,11 @@ The following checks are run if you use the :option:`check --deploy` option:
   ``True``. Unless your site should be available over both SSL and non-SSL
   connections, you may want to either set this setting to ``True`` or configure
   a load balancer or reverse-proxy server  to redirect all connections to HTTPS.
-* **security.W009**: Your :setting:`SECRET_KEY` has less than 50 characters or
-  less than 5 unique characters. Please generate a long and random
-  ``SECRET_KEY``, otherwise many of Django's security-critical features will be
-  vulnerable to attack.
+* **security.W009**: Your :setting:`SECRET_KEY` has less than 50 characters,
+  less than 5 unique characters, or it's prefixed with ``'django-insecure-'``
+  indicating that it was generated automatically by Django. Please generate a
+  long and random ``SECRET_KEY``, otherwise many of Django's security-critical
+  features will be vulnerable to attack.
 * **security.W010**: You have :mod:`django.contrib.sessions` in your
   :setting:`INSTALLED_APPS` but you have not set
   :setting:`SESSION_COOKIE_SECURE` to ``True``. Using a secure-only session

+ 7 - 0
tests/check_framework/test_security.py

@@ -1,5 +1,6 @@
 from django.conf import settings
 from django.core.checks.security import base, csrf, sessions
+from django.core.management.utils import get_random_secret_key
 from django.test import SimpleTestCase
 from django.test.utils import override_settings
 
@@ -394,6 +395,12 @@ class CheckSecretKeyTest(SimpleTestCase):
     def test_none_secret_key(self):
         self.assertEqual(base.check_secret_key(None), [base.W009])
 
+    @override_settings(
+        SECRET_KEY=base.SECRET_KEY_INSECURE_PREFIX + get_random_secret_key()
+    )
+    def test_insecure_secret_key(self):
+        self.assertEqual(base.check_secret_key(None), [base.W009])
+
     @override_settings(SECRET_KEY=('abcdefghijklmnopqrstuvwx' * 2) + 'a')
     def test_low_length_secret_key(self):
         self.assertEqual(len(settings.SECRET_KEY), base.SECRET_KEY_MIN_LENGTH - 1)