123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- from django.http import HttpResponse
- from django.test import RequestFactory, SimpleTestCase
- from django.test.utils import override_settings
- class SecurityMiddlewareTest(SimpleTestCase):
- def middleware(self, *args, **kwargs):
- from django.middleware.security import SecurityMiddleware
- return SecurityMiddleware(self.response(*args, **kwargs))
- @property
- def secure_request_kwargs(self):
- return {"wsgi.url_scheme": "https"}
- def response(self, *args, headers=None, **kwargs):
- def get_response(req):
- response = HttpResponse(*args, **kwargs)
- if headers:
- for k, v in headers.items():
- response[k] = v
- return response
- return get_response
- def process_response(self, *args, secure=False, request=None, **kwargs):
- request_kwargs = {}
- if secure:
- request_kwargs.update(self.secure_request_kwargs)
- if request is None:
- request = self.request.get("/some/url", **request_kwargs)
- ret = self.middleware(*args, **kwargs).process_request(request)
- if ret:
- return ret
- return self.middleware(*args, **kwargs)(request)
- request = RequestFactory()
- def process_request(self, method, *args, secure=False, **kwargs):
- if secure:
- kwargs.update(self.secure_request_kwargs)
- req = getattr(self.request, method.lower())(*args, **kwargs)
- return self.middleware().process_request(req)
- @override_settings(SECURE_HSTS_SECONDS=3600)
- def test_sts_on(self):
- """
- With SECURE_HSTS_SECONDS=3600, the middleware adds
- "Strict-Transport-Security: max-age=3600" to the response.
- """
- self.assertEqual(
- self.process_response(secure=True)["Strict-Transport-Security"],
- 'max-age=3600',
- )
- @override_settings(SECURE_HSTS_SECONDS=3600)
- def test_sts_already_present(self):
- """
- The middleware will not override a "Strict-Transport-Security" header
- already present in the response.
- """
- response = self.process_response(
- secure=True,
- headers={"Strict-Transport-Security": "max-age=7200"})
- self.assertEqual(response["Strict-Transport-Security"], "max-age=7200")
- @override_settings(SECURE_HSTS_SECONDS=3600)
- def test_sts_only_if_secure(self):
- """
- The "Strict-Transport-Security" header is not added to responses going
- over an insecure connection.
- """
- self.assertNotIn("Strict-Transport-Security", self.process_response(secure=False))
- @override_settings(SECURE_HSTS_SECONDS=0)
- def test_sts_off(self):
- """
- With SECURE_HSTS_SECONDS=0, the middleware does not add a
- "Strict-Transport-Security" header to the response.
- """
- self.assertNotIn("Strict-Transport-Security", self.process_response(secure=True))
- @override_settings(SECURE_HSTS_SECONDS=600, SECURE_HSTS_INCLUDE_SUBDOMAINS=True)
- def test_sts_include_subdomains(self):
- """
- With SECURE_HSTS_SECONDS non-zero and SECURE_HSTS_INCLUDE_SUBDOMAINS
- True, the middleware adds a "Strict-Transport-Security" header with the
- "includeSubDomains" directive to the response.
- """
- response = self.process_response(secure=True)
- self.assertEqual(response["Strict-Transport-Security"], "max-age=600; includeSubDomains")
- @override_settings(SECURE_HSTS_SECONDS=600, SECURE_HSTS_INCLUDE_SUBDOMAINS=False)
- def test_sts_no_include_subdomains(self):
- """
- With SECURE_HSTS_SECONDS non-zero and SECURE_HSTS_INCLUDE_SUBDOMAINS
- False, the middleware adds a "Strict-Transport-Security" header without
- the "includeSubDomains" directive to the response.
- """
- 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 SECURE_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 SECURE_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 SECURE_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):
- """
- With SECURE_CONTENT_TYPE_NOSNIFF set to True, the middleware adds
- "X-Content-Type-Options: nosniff" header to the response.
- """
- self.assertEqual(self.process_response()["X-Content-Type-Options"], "nosniff")
- @override_settings(SECURE_CONTENT_TYPE_NOSNIFF=True)
- def test_content_type_already_present(self):
- """
- The middleware will not override an "X-Content-Type-Options" header
- already present in the response.
- """
- response = self.process_response(secure=True, headers={"X-Content-Type-Options": "foo"})
- self.assertEqual(response["X-Content-Type-Options"], "foo")
- @override_settings(SECURE_CONTENT_TYPE_NOSNIFF=False)
- def test_content_type_off(self):
- """
- With SECURE_CONTENT_TYPE_NOSNIFF False, the middleware does not add an
- "X-Content-Type-Options" header to the response.
- """
- self.assertNotIn("X-Content-Type-Options", self.process_response())
- @override_settings(SECURE_BROWSER_XSS_FILTER=True)
- def test_xss_filter_on(self):
- """
- With SECURE_BROWSER_XSS_FILTER set to True, the middleware adds
- "s-xss-protection: 1; mode=block" header to the response.
- """
- self.assertEqual(self.process_response()["X-XSS-Protection"], "1; mode=block")
- @override_settings(SECURE_BROWSER_XSS_FILTER=True)
- def test_xss_filter_already_present(self):
- """
- The middleware will not override an "X-XSS-Protection" header
- already present in the response.
- """
- response = self.process_response(secure=True, headers={"X-XSS-Protection": "foo"})
- self.assertEqual(response["X-XSS-Protection"], "foo")
- @override_settings(SECURE_BROWSER_XSS_FILTER=False)
- def test_xss_filter_off(self):
- """
- With SECURE_BROWSER_XSS_FILTER set to False, the middleware does not
- add an "X-XSS-Protection" header to the response.
- """
- self.assertNotIn("X-XSS-Protection", self.process_response())
- @override_settings(SECURE_SSL_REDIRECT=True)
- def test_ssl_redirect_on(self):
- """
- With SECURE_SSL_REDIRECT True, the middleware redirects any non-secure
- requests to the https:// version of the same URL.
- """
- ret = self.process_request("get", "/some/url?query=string")
- self.assertEqual(ret.status_code, 301)
- self.assertEqual(ret["Location"], "https://testserver/some/url?query=string")
- @override_settings(SECURE_SSL_REDIRECT=True)
- def test_no_redirect_ssl(self):
- """
- The middleware does not redirect secure requests.
- """
- ret = self.process_request("get", "/some/url", secure=True)
- self.assertIsNone(ret)
- @override_settings(SECURE_SSL_REDIRECT=True, SECURE_REDIRECT_EXEMPT=["^insecure/"])
- def test_redirect_exempt(self):
- """
- The middleware does not redirect requests with URL path matching an
- exempt pattern.
- """
- ret = self.process_request("get", "/insecure/page")
- self.assertIsNone(ret)
- @override_settings(SECURE_SSL_REDIRECT=True, SECURE_SSL_HOST="secure.example.com")
- def test_redirect_ssl_host(self):
- """
- The middleware redirects to SECURE_SSL_HOST if given.
- """
- ret = self.process_request("get", "/some/url")
- self.assertEqual(ret.status_code, 301)
- self.assertEqual(ret["Location"], "https://secure.example.com/some/url")
- @override_settings(SECURE_SSL_REDIRECT=False)
- def test_ssl_redirect_off(self):
- """
- With SECURE_SSL_REDIRECT False, the middleware does not redirect.
- """
- ret = self.process_request("get", "/some/url")
- self.assertIsNone(ret)
- @override_settings(SECURE_REFERRER_POLICY=None)
- def test_referrer_policy_off(self):
- """
- With SECURE_REFERRER_POLICY set to None, the middleware does not add a
- "Referrer-Policy" header to the response.
- """
- self.assertNotIn('Referrer-Policy', self.process_response())
- def test_referrer_policy_on(self):
- """
- With SECURE_REFERRER_POLICY set to a valid value, the middleware adds a
- "Referrer-Policy" header to the response.
- """
- tests = (
- ('strict-origin', 'strict-origin'),
- ('strict-origin,origin', 'strict-origin,origin'),
- ('strict-origin, origin', 'strict-origin,origin'),
- (['strict-origin', 'origin'], 'strict-origin,origin'),
- (('strict-origin', 'origin'), 'strict-origin,origin'),
- )
- for value, expected in tests:
- with self.subTest(value=value), override_settings(SECURE_REFERRER_POLICY=value):
- self.assertEqual(self.process_response()['Referrer-Policy'], expected)
- @override_settings(SECURE_REFERRER_POLICY='strict-origin')
- def test_referrer_policy_already_present(self):
- """
- The middleware will not override a "Referrer-Policy" header already
- present in the response.
- """
- response = self.process_response(headers={'Referrer-Policy': 'unsafe-url'})
- self.assertEqual(response['Referrer-Policy'], 'unsafe-url')
|