Skip to content

Commit df7893d

Browse files
committed
implemented async-only security middleware
1 parent dc1d4d4 commit df7893d

File tree

1 file changed

+67
-0
lines changed

1 file changed

+67
-0
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import re
2+
3+
from django.conf import settings
4+
from django.http import HttpResponsePermanentRedirect
5+
6+
from django_async_extensions.amiddleware.base import AsyncMiddlewareMixin
7+
8+
9+
class AsyncSecurityMiddleware(AsyncMiddlewareMixin):
10+
def __init__(self, get_response):
11+
super().__init__(get_response)
12+
self.sts_seconds = settings.SECURE_HSTS_SECONDS
13+
self.sts_include_subdomains = settings.SECURE_HSTS_INCLUDE_SUBDOMAINS
14+
self.sts_preload = settings.SECURE_HSTS_PRELOAD
15+
self.content_type_nosniff = settings.SECURE_CONTENT_TYPE_NOSNIFF
16+
self.redirect = settings.SECURE_SSL_REDIRECT
17+
self.redirect_host = settings.SECURE_SSL_HOST
18+
self.redirect_exempt = [re.compile(r) for r in settings.SECURE_REDIRECT_EXEMPT]
19+
self.referrer_policy = settings.SECURE_REFERRER_POLICY
20+
self.cross_origin_opener_policy = settings.SECURE_CROSS_ORIGIN_OPENER_POLICY
21+
22+
async def process_request(self, request):
23+
path = request.path.lstrip("/")
24+
if (
25+
self.redirect
26+
and not request.is_secure()
27+
and not any(pattern.search(path) for pattern in self.redirect_exempt)
28+
):
29+
host = self.redirect_host or request.get_host()
30+
return HttpResponsePermanentRedirect(
31+
"https://%s%s" % (host, request.get_full_path())
32+
)
33+
34+
async def process_response(self, request, response):
35+
if (
36+
self.sts_seconds
37+
and request.is_secure()
38+
and "Strict-Transport-Security" not in response
39+
):
40+
sts_header = "max-age=%s" % self.sts_seconds
41+
if self.sts_include_subdomains:
42+
sts_header += "; includeSubDomains"
43+
if self.sts_preload:
44+
sts_header += "; preload"
45+
response.headers["Strict-Transport-Security"] = sts_header
46+
47+
if self.content_type_nosniff:
48+
response.headers.setdefault("X-Content-Type-Options", "nosniff")
49+
50+
if self.referrer_policy:
51+
# Support a comma-separated string or iterable of values to allow
52+
# fallback.
53+
response.headers.setdefault(
54+
"Referrer-Policy",
55+
",".join(
56+
[v.strip() for v in self.referrer_policy.split(",")]
57+
if isinstance(self.referrer_policy, str)
58+
else self.referrer_policy
59+
),
60+
)
61+
62+
if self.cross_origin_opener_policy:
63+
response.setdefault(
64+
"Cross-Origin-Opener-Policy",
65+
self.cross_origin_opener_policy,
66+
)
67+
return response

0 commit comments

Comments
 (0)