Jelajahi Sumber

Fixed #26947 -- Added an option to enable the HSTS header preload directive.

Ed Morley 8 tahun lalu
induk
melakukan
3c2447dd13

+ 1 - 0
django/conf/global_settings.py

@@ -629,6 +629,7 @@ SILENCED_SYSTEM_CHECKS = []
 SECURE_BROWSER_XSS_FILTER = False
 SECURE_CONTENT_TYPE_NOSNIFF = False
 SECURE_HSTS_INCLUDE_SUBDOMAINS = False
+SECURE_HSTS_PRELOAD = False
 SECURE_HSTS_SECONDS = 0
 SECURE_REDIRECT_EXEMPT = []
 SECURE_SSL_HOST = None

+ 3 - 2
django/middleware/security.py

@@ -9,6 +9,7 @@ class SecurityMiddleware(MiddlewareMixin):
     def __init__(self, get_response=None):
         self.sts_seconds = settings.SECURE_HSTS_SECONDS
         self.sts_include_subdomains = settings.SECURE_HSTS_INCLUDE_SUBDOMAINS
+        self.sts_preload = settings.SECURE_HSTS_PRELOAD
         self.content_type_nosniff = settings.SECURE_CONTENT_TYPE_NOSNIFF
         self.xss_filter = settings.SECURE_BROWSER_XSS_FILTER
         self.redirect = settings.SECURE_SSL_REDIRECT
@@ -30,10 +31,10 @@ class SecurityMiddleware(MiddlewareMixin):
         if (self.sts_seconds and request.is_secure() and
                 'strict-transport-security' not in response):
             sts_header = "max-age=%s" % self.sts_seconds
-
             if self.sts_include_subdomains:
                 sts_header = sts_header + "; includeSubDomains"
-
+            if self.sts_preload:
+                sts_header = sts_header + "; preload"
             response["strict-transport-security"] = sts_header
 
         if self.content_type_nosniff and 'x-content-type-options' not in response:

+ 6 - 0
docs/ref/middleware.txt

@@ -226,6 +226,7 @@ enabled or disabled with a setting.
 * :setting:`SECURE_BROWSER_XSS_FILTER`
 * :setting:`SECURE_CONTENT_TYPE_NOSNIFF`
 * :setting:`SECURE_HSTS_INCLUDE_SUBDOMAINS`
+* :setting:`SECURE_HSTS_PRELOAD`
 * :setting:`SECURE_HSTS_SECONDS`
 * :setting:`SECURE_REDIRECT_EXEMPT`
 * :setting:`SECURE_SSL_HOST`
@@ -260,6 +261,10 @@ to the ``Strict-Transport-Security`` header. This is recommended (assuming all
 subdomains are served exclusively using HTTPS), otherwise your site may still
 be vulnerable via an insecure connection to a subdomain.
 
+If you wish to submit your site to the `browser preload list`_, set the
+:setting:`SECURE_HSTS_PRELOAD` setting to ``True``. That appends the
+``preload`` directive to the ``Strict-Transport-Security`` header.
+
 .. warning::
     The HSTS policy applies to your entire domain, not just the URL of the
     response that you set the header on. Therefore, you should only use it if
@@ -277,6 +282,7 @@ be vulnerable via an insecure connection to a subdomain.
     you may need to set the :setting:`SECURE_PROXY_SSL_HEADER` setting.
 
 .. _"Strict-Transport-Security" header: https://en.wikipedia.org/wiki/Strict_Transport_Security
+.. _browser preload list: https://hstspreload.appspot.com/
 
 .. _x-content-type-options:
 

+ 20 - 0
docs/ref/settings.txt

@@ -2062,6 +2062,25 @@ non-zero value.
     :setting:`SECURE_HSTS_SECONDS`) break your site. Read the
     :ref:`http-strict-transport-security` documentation first.
 
+.. setting:: SECURE_HSTS_PRELOAD
+
+``SECURE_HSTS_PRELOAD``
+-----------------------
+
+.. versionadded:: 1.11
+
+Default: ``False``
+
+If ``True``, the :class:`~django.middleware.security.SecurityMiddleware` adds
+the ``preload`` directive to the :ref:`http-strict-transport-security`
+header. It has no effect unless :setting:`SECURE_HSTS_SECONDS` is set to a
+non-zero value.
+
+.. warning::
+    Setting this incorrectly can irreversibly (for at least several months,
+    depending on browser releases) break your site. Read the
+    :ref:`http-strict-transport-security` documentation first.
+
 .. setting:: SECURE_HSTS_SECONDS
 
 ``SECURE_HSTS_SECONDS``
@@ -3334,6 +3353,7 @@ HTTP
   * :setting:`SECURE_BROWSER_XSS_FILTER`
   * :setting:`SECURE_CONTENT_TYPE_NOSNIFF`
   * :setting:`SECURE_HSTS_INCLUDE_SUBDOMAINS`
+  * :setting:`SECURE_HSTS_PRELOAD`
   * :setting:`SECURE_HSTS_SECONDS`
   * :setting:`SECURE_PROXY_SSL_HEADER`
   * :setting:`SECURE_REDIRECT_EXEMPT`

+ 3 - 0
docs/releases/1.11.txt

@@ -229,6 +229,9 @@ Requests and Responses
 * :class:`~django.middleware.common.CommonMiddleware` now sets the
   ``Content-Length`` response header for non-streaming responses.
 
+* Added the :setting:`SECURE_HSTS_PRELOAD` setting to allow appending the
+  ``preload`` directive to the ``Strict-Transport-Security`` header.
+
 Serialization
 ~~~~~~~~~~~~~
 

+ 3 - 2
docs/topics/security.txt

@@ -160,8 +160,9 @@ server, there are some additional steps you may need:
   to a particular site should always use HTTPS. Combined with redirecting
   requests over HTTP to HTTPS, this will ensure that connections always enjoy
   the added security of SSL provided one successful connection has occurred.
-  HSTS may either be configured with :setting:`SECURE_HSTS_SECONDS` and
-  :setting:`SECURE_HSTS_INCLUDE_SUBDOMAINS` or on the Web server.
+  HSTS may either be configured with :setting:`SECURE_HSTS_SECONDS`,
+  :setting:`SECURE_HSTS_INCLUDE_SUBDOMAINS`, and :setting:`SECURE_HSTS_PRELOAD`,
+  or on the Web server.
 
 .. _host-headers-virtual-hosting:
 

+ 31 - 0
tests/middleware/test_security.py

@@ -99,6 +99,37 @@ class SecurityMiddlewareTest(SimpleTestCase):
         response = self.process_response(secure=True)
         self.assertEqual(response["strict-transport-security"], "max-age=600")
 
+    @override_settings(SECURE_HSTS_SECONDS=10886400, SECURE_HSTS_PRELOAD=True)
+    def test_sts_preload(self):
+        """
+        With HSTS_SECONDS non-zero and SECURE_HSTS_PRELOAD True, the middleware
+        adds a "strict-transport-security" header with the "preload" directive
+        to the response.
+        """
+        response = self.process_response(secure=True)
+        self.assertEqual(response["strict-transport-security"], "max-age=10886400; preload")
+
+    @override_settings(SECURE_HSTS_SECONDS=10886400, SECURE_HSTS_INCLUDE_SUBDOMAINS=True, SECURE_HSTS_PRELOAD=True)
+    def test_sts_subdomains_and_preload(self):
+        """
+        With HSTS_SECONDS non-zero, SECURE_HSTS_INCLUDE_SUBDOMAINS and
+        SECURE_HSTS_PRELOAD True, the middleware adds a "strict-transport-security"
+        header containing both the "includeSubDomains" and "preload" directives
+        to the response.
+        """
+        response = self.process_response(secure=True)
+        self.assertEqual(response["strict-transport-security"], "max-age=10886400; includeSubDomains; preload")
+
+    @override_settings(SECURE_HSTS_SECONDS=10886400, SECURE_HSTS_PRELOAD=False)
+    def test_sts_no_preload(self):
+        """
+        With HSTS_SECONDS non-zero and SECURE_HSTS_PRELOAD
+        False, the middleware adds a "strict-transport-security" header without
+        the "preload" directive to the response.
+        """
+        response = self.process_response(secure=True)
+        self.assertEqual(response["strict-transport-security"], "max-age=10886400")
+
     @override_settings(SECURE_CONTENT_TYPE_NOSNIFF=True)
     def test_content_type_on(self):
         """