mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 11:16:47 +00:00
feat(security): every page carries a Content-Security-Policy that needs no template change
A small middleware sends object-src 'none', base-uri 'self', form-action 'self' and frame-ancestors 'none': no plugin content, no injected <base> redirecting relative URLs, no form posting to another origin, no framing. script-src and style-src are left out: the templates still hold inline scripts, event handlers and javascript: URLs, which an enforced script policy would block. The middleware is added to the settings template, which the update steps and each Docker container rebuild settings.py from.
This commit is contained in:
parent
75559629e4
commit
6f44461c28
4 changed files with 30 additions and 0 deletions
|
|
@ -311,6 +311,8 @@ For a panel served over HTTPS:
|
|||
|
||||
TLS can end at the bundled nginx or at a proxy in front of it; such a proxy must send `X-Forwarded-Proto: https`. Failed logins are counted per client address, which the bundled nginx passes in `X-Real-IP`. A proxy that replaces the bundled nginx must set `X-Real-IP` itself, and its address must be in `LOGIN_TRUSTED_PROXIES` in `settings.py`. With a proxy in front of the bundled nginx, configure nginx's `set_real_ip_from` / `real_ip_header` for that proxy, or all clients share its address.
|
||||
|
||||
Every page is sent with `X-Frame-Options: DENY` and `Content-Security-Policy: object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'`: the panel cannot be shown in a frame, and its forms post only to its own origin.
|
||||
|
||||
### Cloud-init datasource
|
||||
|
||||
WebVirtCloud serves cloud-init metadata (root SSH keys and hostname) to guest instances. The host in the URL must be in `ALLOWED_HOSTS`:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ from operations import claims, jobs
|
|||
from operations.utils import busy_message, public_error_text, summary
|
||||
|
||||
API_PREFIX = "/api/"
|
||||
# the directives that need no change to the templates' inline scripts and styles
|
||||
CONTENT_SECURITY_POLICY = "object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'"
|
||||
|
||||
|
||||
def error_text(request, exception):
|
||||
|
|
@ -21,6 +23,16 @@ def error_text(request, exception):
|
|||
return public_error_text(exception)
|
||||
|
||||
|
||||
class ContentSecurityPolicyMiddleware:
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
response = self.get_response(request)
|
||||
response.setdefault("Content-Security-Policy", CONTENT_SECURITY_POLICY)
|
||||
return response
|
||||
|
||||
|
||||
class ExceptionMiddleware:
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ MIDDLEWARE = [
|
|||
"django.contrib.auth.middleware.RemoteUserMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"webvirtcloud.middleware.ContentSecurityPolicyMiddleware",
|
||||
"appsettings.middleware.AppSettingsMiddleware",
|
||||
"webvirtcloud.middleware.ExceptionMiddleware",
|
||||
# last, as django-axes requires
|
||||
|
|
|
|||
15
webvirtcloud/tests/test_csp.py
Normal file
15
webvirtcloud/tests/test_csp.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
"""Every response carries the Content-Security-Policy directives that need no
|
||||
change to the templates' inline scripts and styles."""
|
||||
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
|
||||
class ContentSecurityPolicyTests(TestCase):
|
||||
def test_pages_and_redirects_carry_the_policy(self):
|
||||
for url in (reverse("accounts:login"), reverse("index")): # a page, and a redirect to the login
|
||||
with self.subTest(url=url):
|
||||
policy = self.client.get(url)["Content-Security-Policy"]
|
||||
self.assertEqual(
|
||||
policy, "object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'"
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue