test_security.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. from django.http import HttpResponse
  2. from django.test import RequestFactory, SimpleTestCase
  3. from django.test.utils import override_settings
  4. class SecurityMiddlewareTest(SimpleTestCase):
  5. def middleware(self, *args, **kwargs):
  6. from django.middleware.security import SecurityMiddleware
  7. return SecurityMiddleware(self.response(*args, **kwargs))
  8. @property
  9. def secure_request_kwargs(self):
  10. return {"wsgi.url_scheme": "https"}
  11. def response(self, *args, headers=None, **kwargs):
  12. def get_response(req):
  13. response = HttpResponse(*args, **kwargs)
  14. if headers:
  15. for k, v in headers.items():
  16. response[k] = v
  17. return response
  18. return get_response
  19. def process_response(self, *args, secure=False, request=None, **kwargs):
  20. request_kwargs = {}
  21. if secure:
  22. request_kwargs.update(self.secure_request_kwargs)
  23. if request is None:
  24. request = self.request.get("/some/url", **request_kwargs)
  25. ret = self.middleware(*args, **kwargs).process_request(request)
  26. if ret:
  27. return ret
  28. return self.middleware(*args, **kwargs)(request)
  29. request = RequestFactory()
  30. def process_request(self, method, *args, secure=False, **kwargs):
  31. if secure:
  32. kwargs.update(self.secure_request_kwargs)
  33. req = getattr(self.request, method.lower())(*args, **kwargs)
  34. return self.middleware().process_request(req)
  35. @override_settings(SECURE_HSTS_SECONDS=3600)
  36. def test_sts_on(self):
  37. """
  38. With SECURE_HSTS_SECONDS=3600, the middleware adds
  39. "Strict-Transport-Security: max-age=3600" to the response.
  40. """
  41. self.assertEqual(
  42. self.process_response(secure=True)["Strict-Transport-Security"],
  43. 'max-age=3600',
  44. )
  45. @override_settings(SECURE_HSTS_SECONDS=3600)
  46. def test_sts_already_present(self):
  47. """
  48. The middleware will not override a "Strict-Transport-Security" header
  49. already present in the response.
  50. """
  51. response = self.process_response(
  52. secure=True,
  53. headers={"Strict-Transport-Security": "max-age=7200"})
  54. self.assertEqual(response["Strict-Transport-Security"], "max-age=7200")
  55. @override_settings(SECURE_HSTS_SECONDS=3600)
  56. def test_sts_only_if_secure(self):
  57. """
  58. The "Strict-Transport-Security" header is not added to responses going
  59. over an insecure connection.
  60. """
  61. self.assertNotIn("Strict-Transport-Security", self.process_response(secure=False))
  62. @override_settings(SECURE_HSTS_SECONDS=0)
  63. def test_sts_off(self):
  64. """
  65. With SECURE_HSTS_SECONDS=0, the middleware does not add a
  66. "Strict-Transport-Security" header to the response.
  67. """
  68. self.assertNotIn("Strict-Transport-Security", self.process_response(secure=True))
  69. @override_settings(SECURE_HSTS_SECONDS=600, SECURE_HSTS_INCLUDE_SUBDOMAINS=True)
  70. def test_sts_include_subdomains(self):
  71. """
  72. With SECURE_HSTS_SECONDS non-zero and SECURE_HSTS_INCLUDE_SUBDOMAINS
  73. True, the middleware adds a "Strict-Transport-Security" header with the
  74. "includeSubDomains" directive to the response.
  75. """
  76. response = self.process_response(secure=True)
  77. self.assertEqual(response["Strict-Transport-Security"], "max-age=600; includeSubDomains")
  78. @override_settings(SECURE_HSTS_SECONDS=600, SECURE_HSTS_INCLUDE_SUBDOMAINS=False)
  79. def test_sts_no_include_subdomains(self):
  80. """
  81. With SECURE_HSTS_SECONDS non-zero and SECURE_HSTS_INCLUDE_SUBDOMAINS
  82. False, the middleware adds a "Strict-Transport-Security" header without
  83. the "includeSubDomains" directive to the response.
  84. """
  85. response = self.process_response(secure=True)
  86. self.assertEqual(response["Strict-Transport-Security"], "max-age=600")
  87. @override_settings(SECURE_HSTS_SECONDS=10886400, SECURE_HSTS_PRELOAD=True)
  88. def test_sts_preload(self):
  89. """
  90. With SECURE_HSTS_SECONDS non-zero and SECURE_HSTS_PRELOAD True, the
  91. middleware adds a "Strict-Transport-Security" header with the "preload"
  92. directive to the response.
  93. """
  94. response = self.process_response(secure=True)
  95. self.assertEqual(response["Strict-Transport-Security"], "max-age=10886400; preload")
  96. @override_settings(SECURE_HSTS_SECONDS=10886400, SECURE_HSTS_INCLUDE_SUBDOMAINS=True, SECURE_HSTS_PRELOAD=True)
  97. def test_sts_subdomains_and_preload(self):
  98. """
  99. With SECURE_HSTS_SECONDS non-zero, SECURE_HSTS_INCLUDE_SUBDOMAINS and
  100. SECURE_HSTS_PRELOAD True, the middleware adds a "Strict-Transport-Security"
  101. header containing both the "includeSubDomains" and "preload" directives
  102. to the response.
  103. """
  104. response = self.process_response(secure=True)
  105. self.assertEqual(response["Strict-Transport-Security"], "max-age=10886400; includeSubDomains; preload")
  106. @override_settings(SECURE_HSTS_SECONDS=10886400, SECURE_HSTS_PRELOAD=False)
  107. def test_sts_no_preload(self):
  108. """
  109. With SECURE_HSTS_SECONDS non-zero and SECURE_HSTS_PRELOAD
  110. False, the middleware adds a "Strict-Transport-Security" header without
  111. the "preload" directive to the response.
  112. """
  113. response = self.process_response(secure=True)
  114. self.assertEqual(response["Strict-Transport-Security"], "max-age=10886400")
  115. @override_settings(SECURE_CONTENT_TYPE_NOSNIFF=True)
  116. def test_content_type_on(self):
  117. """
  118. With SECURE_CONTENT_TYPE_NOSNIFF set to True, the middleware adds
  119. "X-Content-Type-Options: nosniff" header to the response.
  120. """
  121. self.assertEqual(self.process_response()["X-Content-Type-Options"], "nosniff")
  122. @override_settings(SECURE_CONTENT_TYPE_NOSNIFF=True)
  123. def test_content_type_already_present(self):
  124. """
  125. The middleware will not override an "X-Content-Type-Options" header
  126. already present in the response.
  127. """
  128. response = self.process_response(secure=True, headers={"X-Content-Type-Options": "foo"})
  129. self.assertEqual(response["X-Content-Type-Options"], "foo")
  130. @override_settings(SECURE_CONTENT_TYPE_NOSNIFF=False)
  131. def test_content_type_off(self):
  132. """
  133. With SECURE_CONTENT_TYPE_NOSNIFF False, the middleware does not add an
  134. "X-Content-Type-Options" header to the response.
  135. """
  136. self.assertNotIn("X-Content-Type-Options", self.process_response())
  137. @override_settings(SECURE_BROWSER_XSS_FILTER=True)
  138. def test_xss_filter_on(self):
  139. """
  140. With SECURE_BROWSER_XSS_FILTER set to True, the middleware adds
  141. "s-xss-protection: 1; mode=block" header to the response.
  142. """
  143. self.assertEqual(self.process_response()["X-XSS-Protection"], "1; mode=block")
  144. @override_settings(SECURE_BROWSER_XSS_FILTER=True)
  145. def test_xss_filter_already_present(self):
  146. """
  147. The middleware will not override an "X-XSS-Protection" header
  148. already present in the response.
  149. """
  150. response = self.process_response(secure=True, headers={"X-XSS-Protection": "foo"})
  151. self.assertEqual(response["X-XSS-Protection"], "foo")
  152. @override_settings(SECURE_BROWSER_XSS_FILTER=False)
  153. def test_xss_filter_off(self):
  154. """
  155. With SECURE_BROWSER_XSS_FILTER set to False, the middleware does not
  156. add an "X-XSS-Protection" header to the response.
  157. """
  158. self.assertNotIn("X-XSS-Protection", self.process_response())
  159. @override_settings(SECURE_SSL_REDIRECT=True)
  160. def test_ssl_redirect_on(self):
  161. """
  162. With SECURE_SSL_REDIRECT True, the middleware redirects any non-secure
  163. requests to the https:// version of the same URL.
  164. """
  165. ret = self.process_request("get", "/some/url?query=string")
  166. self.assertEqual(ret.status_code, 301)
  167. self.assertEqual(ret["Location"], "https://testserver/some/url?query=string")
  168. @override_settings(SECURE_SSL_REDIRECT=True)
  169. def test_no_redirect_ssl(self):
  170. """
  171. The middleware does not redirect secure requests.
  172. """
  173. ret = self.process_request("get", "/some/url", secure=True)
  174. self.assertIsNone(ret)
  175. @override_settings(SECURE_SSL_REDIRECT=True, SECURE_REDIRECT_EXEMPT=["^insecure/"])
  176. def test_redirect_exempt(self):
  177. """
  178. The middleware does not redirect requests with URL path matching an
  179. exempt pattern.
  180. """
  181. ret = self.process_request("get", "/insecure/page")
  182. self.assertIsNone(ret)
  183. @override_settings(SECURE_SSL_REDIRECT=True, SECURE_SSL_HOST="secure.example.com")
  184. def test_redirect_ssl_host(self):
  185. """
  186. The middleware redirects to SECURE_SSL_HOST if given.
  187. """
  188. ret = self.process_request("get", "/some/url")
  189. self.assertEqual(ret.status_code, 301)
  190. self.assertEqual(ret["Location"], "https://secure.example.com/some/url")
  191. @override_settings(SECURE_SSL_REDIRECT=False)
  192. def test_ssl_redirect_off(self):
  193. """
  194. With SECURE_SSL_REDIRECT False, the middleware does not redirect.
  195. """
  196. ret = self.process_request("get", "/some/url")
  197. self.assertIsNone(ret)
  198. @override_settings(SECURE_REFERRER_POLICY=None)
  199. def test_referrer_policy_off(self):
  200. """
  201. With SECURE_REFERRER_POLICY set to None, the middleware does not add a
  202. "Referrer-Policy" header to the response.
  203. """
  204. self.assertNotIn('Referrer-Policy', self.process_response())
  205. def test_referrer_policy_on(self):
  206. """
  207. With SECURE_REFERRER_POLICY set to a valid value, the middleware adds a
  208. "Referrer-Policy" header to the response.
  209. """
  210. tests = (
  211. ('strict-origin', 'strict-origin'),
  212. ('strict-origin,origin', 'strict-origin,origin'),
  213. ('strict-origin, origin', 'strict-origin,origin'),
  214. (['strict-origin', 'origin'], 'strict-origin,origin'),
  215. (('strict-origin', 'origin'), 'strict-origin,origin'),
  216. )
  217. for value, expected in tests:
  218. with self.subTest(value=value), override_settings(SECURE_REFERRER_POLICY=value):
  219. self.assertEqual(self.process_response()['Referrer-Policy'], expected)
  220. @override_settings(SECURE_REFERRER_POLICY='strict-origin')
  221. def test_referrer_policy_already_present(self):
  222. """
  223. The middleware will not override a "Referrer-Policy" header already
  224. present in the response.
  225. """
  226. response = self.process_response(headers={'Referrer-Policy': 'unsafe-url'})
  227. self.assertEqual(response['Referrer-Policy'], 'unsafe-url')