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

This commit is contained in:
Ed Morley 2016-07-28 17:48:07 +01:00 committed by Tim Graham
parent c412aaca73
commit 3c2447dd13
7 changed files with 67 additions and 4 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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