security.py 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. import re
  2. from django.conf import settings
  3. from django.http import HttpResponsePermanentRedirect
  4. from django.utils.deprecation import MiddlewareMixin
  5. class SecurityMiddleware(MiddlewareMixin):
  6. # RemovedInDjango40Warning: when the deprecation ends, replace with:
  7. # def __init__(self, get_response):
  8. def __init__(self, get_response=None):
  9. super().__init__(get_response)
  10. self.sts_seconds = settings.SECURE_HSTS_SECONDS
  11. self.sts_include_subdomains = settings.SECURE_HSTS_INCLUDE_SUBDOMAINS
  12. self.sts_preload = settings.SECURE_HSTS_PRELOAD
  13. self.content_type_nosniff = settings.SECURE_CONTENT_TYPE_NOSNIFF
  14. self.xss_filter = settings.SECURE_BROWSER_XSS_FILTER
  15. self.redirect = settings.SECURE_SSL_REDIRECT
  16. self.redirect_host = settings.SECURE_SSL_HOST
  17. self.redirect_exempt = [re.compile(r) for r in settings.SECURE_REDIRECT_EXEMPT]
  18. self.referrer_policy = settings.SECURE_REFERRER_POLICY
  19. def process_request(self, request):
  20. path = request.path.lstrip("/")
  21. if (self.redirect and not request.is_secure() and
  22. not any(pattern.search(path)
  23. for pattern in self.redirect_exempt)):
  24. host = self.redirect_host or request.get_host()
  25. return HttpResponsePermanentRedirect(
  26. "https://%s%s" % (host, request.get_full_path())
  27. )
  28. def process_response(self, request, response):
  29. if (self.sts_seconds and request.is_secure() and
  30. 'Strict-Transport-Security' not in response):
  31. sts_header = "max-age=%s" % self.sts_seconds
  32. if self.sts_include_subdomains:
  33. sts_header = sts_header + "; includeSubDomains"
  34. if self.sts_preload:
  35. sts_header = sts_header + "; preload"
  36. response.headers['Strict-Transport-Security'] = sts_header
  37. if self.content_type_nosniff:
  38. response.headers.setdefault('X-Content-Type-Options', 'nosniff')
  39. if self.xss_filter:
  40. response.headers.setdefault('X-XSS-Protection', '1; mode=block')
  41. if self.referrer_policy:
  42. # Support a comma-separated string or iterable of values to allow
  43. # fallback.
  44. response.headers.setdefault('Referrer-Policy', ','.join(
  45. [v.strip() for v in self.referrer_policy.split(',')]
  46. if isinstance(self.referrer_policy, str) else self.referrer_policy
  47. ))
  48. return response