test_security.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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. @property
  6. def middleware(self):
  7. from django.middleware.security import SecurityMiddleware
  8. return SecurityMiddleware()
  9. @property
  10. def secure_request_kwargs(self):
  11. return {"wsgi.url_scheme": "https"}
  12. def response(self, *args, **kwargs):
  13. headers = kwargs.pop("headers", {})
  14. response = HttpResponse(*args, **kwargs)
  15. for k, v in headers.items():
  16. response[k] = v
  17. return response
  18. def process_response(self, *args, **kwargs):
  19. request_kwargs = {}
  20. if kwargs.pop("secure", False):
  21. request_kwargs.update(self.secure_request_kwargs)
  22. request = (kwargs.pop("request", None) or
  23. self.request.get("/some/url", **request_kwargs))
  24. ret = self.middleware.process_request(request)
  25. if ret:
  26. return ret
  27. return self.middleware.process_response(
  28. request, self.response(*args, **kwargs))
  29. request = RequestFactory()
  30. def process_request(self, method, *args, **kwargs):
  31. if kwargs.pop("secure", False):
  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 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. @override_settings(SECURE_HSTS_SECONDS=3600)
  45. def test_sts_already_present(self):
  46. """
  47. The middleware will not override a "strict-transport-security" header
  48. already present in the response.
  49. """
  50. response = self.process_response(
  51. secure=True,
  52. headers={"strict-transport-security": "max-age=7200"})
  53. self.assertEqual(response["strict-transport-security"], "max-age=7200")
  54. @override_settings(HSTS_SECONDS=3600)
  55. def test_sts_only_if_secure(self):
  56. """
  57. The "strict-transport-security" header is not added to responses going
  58. over an insecure connection.
  59. """
  60. self.assertNotIn("strict-transport-security", self.process_response(secure=False))
  61. @override_settings(HSTS_SECONDS=0)
  62. def test_sts_off(self):
  63. """
  64. With HSTS_SECONDS of 0, the middleware does not add a
  65. "strict-transport-security" header to the response.
  66. """
  67. self.assertNotIn("strict-transport-security", self.process_response(secure=True))
  68. @override_settings(
  69. SECURE_HSTS_SECONDS=600, SECURE_HSTS_INCLUDE_SUBDOMAINS=True)
  70. def test_sts_include_subdomains(self):
  71. """
  72. With HSTS_SECONDS non-zero and 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(
  79. SECURE_HSTS_SECONDS=600, SECURE_HSTS_INCLUDE_SUBDOMAINS=False)
  80. def test_sts_no_include_subdomains(self):
  81. """
  82. With HSTS_SECONDS non-zero and HSTS_INCLUDE_SUBDOMAINS
  83. False, the middleware adds a "strict-transport-security" header without
  84. the "includeSubDomains" directive to the response.
  85. """
  86. response = self.process_response(secure=True)
  87. self.assertEqual(response["strict-transport-security"], "max-age=600")
  88. @override_settings(SECURE_HSTS_SECONDS=10886400, SECURE_HSTS_PRELOAD=True)
  89. def test_sts_preload(self):
  90. """
  91. With HSTS_SECONDS non-zero and SECURE_HSTS_PRELOAD True, the middleware
  92. adds a "strict-transport-security" header with the "preload" directive
  93. to the response.
  94. """
  95. response = self.process_response(secure=True)
  96. self.assertEqual(response["strict-transport-security"], "max-age=10886400; preload")
  97. @override_settings(SECURE_HSTS_SECONDS=10886400, SECURE_HSTS_INCLUDE_SUBDOMAINS=True, SECURE_HSTS_PRELOAD=True)
  98. def test_sts_subdomains_and_preload(self):
  99. """
  100. With HSTS_SECONDS non-zero, SECURE_HSTS_INCLUDE_SUBDOMAINS and
  101. SECURE_HSTS_PRELOAD True, the middleware adds a "strict-transport-security"
  102. header containing both the "includeSubDomains" and "preload" directives
  103. to the response.
  104. """
  105. response = self.process_response(secure=True)
  106. self.assertEqual(response["strict-transport-security"], "max-age=10886400; includeSubDomains; preload")
  107. @override_settings(SECURE_HSTS_SECONDS=10886400, SECURE_HSTS_PRELOAD=False)
  108. def test_sts_no_preload(self):
  109. """
  110. With HSTS_SECONDS non-zero and SECURE_HSTS_PRELOAD
  111. False, the middleware adds a "strict-transport-security" header without
  112. the "preload" directive to the response.
  113. """
  114. response = self.process_response(secure=True)
  115. self.assertEqual(response["strict-transport-security"], "max-age=10886400")
  116. @override_settings(SECURE_CONTENT_TYPE_NOSNIFF=True)
  117. def test_content_type_on(self):
  118. """
  119. With CONTENT_TYPE_NOSNIFF set to True, the middleware adds
  120. "x-content-type-options: nosniff" header to the response.
  121. """
  122. self.assertEqual(self.process_response()["x-content-type-options"], "nosniff")
  123. @override_settings(SECURE_CONTENT_TYPE_NOSNIFF=True)
  124. def test_content_type_already_present(self):
  125. """
  126. The middleware will not override an "x-content-type-options" header
  127. already present in the response.
  128. """
  129. response = self.process_response(secure=True, headers={"x-content-type-options": "foo"})
  130. self.assertEqual(response["x-content-type-options"], "foo")
  131. @override_settings(SECURE_CONTENT_TYPE_NOSNIFF=False)
  132. def test_content_type_off(self):
  133. """
  134. With CONTENT_TYPE_NOSNIFF False, the middleware does not add an
  135. "x-content-type-options" header to the response.
  136. """
  137. self.assertNotIn("x-content-type-options", self.process_response())
  138. @override_settings(SECURE_BROWSER_XSS_FILTER=True)
  139. def test_xss_filter_on(self):
  140. """
  141. With BROWSER_XSS_FILTER set to True, the middleware adds
  142. "s-xss-protection: 1; mode=block" header to the response.
  143. """
  144. self.assertEqual(
  145. self.process_response()["x-xss-protection"],
  146. "1; mode=block")
  147. @override_settings(SECURE_BROWSER_XSS_FILTER=True)
  148. def test_xss_filter_already_present(self):
  149. """
  150. The middleware will not override an "x-xss-protection" header
  151. already present in the response.
  152. """
  153. response = self.process_response(secure=True, headers={"x-xss-protection": "foo"})
  154. self.assertEqual(response["x-xss-protection"], "foo")
  155. @override_settings(BROWSER_XSS_FILTER=False)
  156. def test_xss_filter_off(self):
  157. """
  158. With BROWSER_XSS_FILTER set to False, the middleware does not add an
  159. "x-xss-protection" header to the response.
  160. """
  161. self.assertNotIn("x-xss-protection", self.process_response())
  162. @override_settings(SECURE_SSL_REDIRECT=True)
  163. def test_ssl_redirect_on(self):
  164. """
  165. With SSL_REDIRECT True, the middleware redirects any non-secure
  166. requests to the https:// version of the same URL.
  167. """
  168. ret = self.process_request("get", "/some/url?query=string")
  169. self.assertEqual(ret.status_code, 301)
  170. self.assertEqual(
  171. ret["Location"], "https://testserver/some/url?query=string")
  172. @override_settings(SECURE_SSL_REDIRECT=True)
  173. def test_no_redirect_ssl(self):
  174. """
  175. The middleware does not redirect secure requests.
  176. """
  177. ret = self.process_request("get", "/some/url", secure=True)
  178. self.assertIsNone(ret)
  179. @override_settings(
  180. SECURE_SSL_REDIRECT=True, SECURE_REDIRECT_EXEMPT=["^insecure/"])
  181. def test_redirect_exempt(self):
  182. """
  183. The middleware does not redirect requests with URL path matching an
  184. exempt pattern.
  185. """
  186. ret = self.process_request("get", "/insecure/page")
  187. self.assertIsNone(ret)
  188. @override_settings(
  189. SECURE_SSL_REDIRECT=True, SECURE_SSL_HOST="secure.example.com")
  190. def test_redirect_ssl_host(self):
  191. """
  192. The middleware redirects to SSL_HOST if given.
  193. """
  194. ret = self.process_request("get", "/some/url")
  195. self.assertEqual(ret.status_code, 301)
  196. self.assertEqual(ret["Location"], "https://secure.example.com/some/url")
  197. @override_settings(SECURE_SSL_REDIRECT=False)
  198. def test_ssl_redirect_off(self):
  199. """
  200. With SSL_REDIRECT False, the middleware does no redirect.
  201. """
  202. ret = self.process_request("get", "/some/url")
  203. self.assertIsNone(ret)