1
0
mirror of https://github.com/django/django.git synced 2024-12-23 01:25:58 +00:00

Refs #15727 -- Add support for Content-Security-Policy (CSP) to SecurityMiddleware

This commit is contained in:
= 2023-05-17 11:36:16 +05:30
parent 0ec60661e6
commit 87bb837e18
4 changed files with 61 additions and 0 deletions

View File

@ -665,3 +665,8 @@ SECURE_REDIRECT_EXEMPT = []
SECURE_REFERRER_POLICY = "same-origin" SECURE_REFERRER_POLICY = "same-origin"
SECURE_SSL_HOST = None SECURE_SSL_HOST = None
SECURE_SSL_REDIRECT = False SECURE_SSL_REDIRECT = False
SECURE_CSP = {}
SECURE_CSP_REPORT_ONLY = False
SECURE_CSP_MULTIPLE = None
SECURE_CSP_INCLUDE_NONCE_IN = None
SECURE_CSP_EXCLUDE_URL_PREFIXES = ()

View File

@ -17,6 +17,11 @@ class SecurityMiddleware(MiddlewareMixin):
self.redirect_exempt = [re.compile(r) for r in settings.SECURE_REDIRECT_EXEMPT] self.redirect_exempt = [re.compile(r) for r in settings.SECURE_REDIRECT_EXEMPT]
self.referrer_policy = settings.SECURE_REFERRER_POLICY self.referrer_policy = settings.SECURE_REFERRER_POLICY
self.cross_origin_opener_policy = settings.SECURE_CROSS_ORIGIN_OPENER_POLICY self.cross_origin_opener_policy = settings.SECURE_CROSS_ORIGIN_OPENER_POLICY
self.csp = settings.SECURE_CSP
self.csp_multiple = settings.SECURE_CSP_MULTIPLE
self.csp_report_only = settings.SECURE_CSP_REPORT_ONLY
self.csp_nonce = settings.SECURE_CSP_INCLUDE_NONCE_IN
self.csp_exclude_url_prefixes = settings.SECURE_CSP_EXCLUDE_URL_PREFIXES
def process_request(self, request): def process_request(self, request):
path = request.path.lstrip("/") path = request.path.lstrip("/")
@ -63,4 +68,33 @@ class SecurityMiddleware(MiddlewareMixin):
"Cross-Origin-Opener-Policy", "Cross-Origin-Opener-Policy",
self.cross_origin_opener_policy, self.cross_origin_opener_policy,
) )
if request.path_info.startswith(self.csp_exclude_url_prefixes):
return response
if self.csp:
header = "Content-Security-Policy"
csp_header_value = "; ".join((f"{k} {v}" for k, v in self.csp.items()))
if self.csp_report_only:
header += "-Report-Only"
if self.csp_nonce:
nonce = getattr(request, "_csp_nonce", None)
csp_header_value += "; 'nonce-%s'" % nonce
response.headers[header] = csp_header_value
if self.csp_multiple:
# Support a comma-separated string or iterable of values to allow
# fallback.
header = "Content-Security-Policy"
csp_header_value = "; ".join(
[v.strip() for v in self.csp_multiple.split(";")]
if isinstance(self.csp_multiple, str)
else self.csp_multiple
)
if self.csp_report_only:
header += "-Report-Only"
response.headers[header] = csp_header_value
return response return response

View File

@ -87,3 +87,9 @@ def media(request):
def request(request): def request(request):
return {"request": request} return {"request": request}
def nonce(request):
nonce = request.csp_nonce if hasattr(request, "csp_nonce") else ""
return {"CSP_NONCE": nonce}

View File

@ -0,0 +1,16 @@
from functools import wraps
def csp(**kwargs):
csp_header_value = "; ".join((f"{k} {v}" for k, v in kwargs.items()))
def decorator(f):
@wraps(f)
def _wrapped(*a, **kw):
resp = f(*a, **kw) # response object from the view
resp["Content-Security-Policy"] = csp_header_value
return resp
return _wrapped
return decorator