diff --git a/src/pretix/base/middleware.py b/src/pretix/base/middleware.py
index a656ae94e..5808d18fb 100644
--- a/src/pretix/base/middleware.py
+++ b/src/pretix/base/middleware.py
@@ -7,6 +7,7 @@ from django.core.urlresolvers import get_script_prefix
from django.http import HttpRequest, HttpResponse
from django.utils import timezone, translation
from django.utils.cache import patch_vary_headers
+from django.utils.crypto import get_random_string
from django.utils.deprecation import MiddlewareMixin
from django.utils.translation import LANGUAGE_SESSION_KEY
from django.utils.translation.trans_real import (
@@ -165,6 +166,9 @@ class SecurityMiddleware(MiddlewareMixin):
'/api/v1/docs/',
)
+ def process_request(self, request):
+ request.csp_nonce = get_random_string(length=32)
+
def process_response(self, request, resp):
if settings.DEBUG and resp.status_code >= 400:
# Don't use CSP on debug error page as it breaks of Django's fancy error
@@ -179,9 +183,9 @@ class SecurityMiddleware(MiddlewareMixin):
# frame-src is deprecated but kept for compatibility with CSP 1.0 browsers, e.g. Safari 9
'frame-src': ['{static}', 'https://checkout.stripe.com', 'https://js.stripe.com'],
'child-src': ['{static}', 'https://checkout.stripe.com', 'https://js.stripe.com'],
- 'style-src': ["{static}"],
+ 'style-src': ["{static}", "'nonce-{nonce}'"],
'connect-src': ["{dynamic}", "https://checkout.stripe.com"],
- 'img-src': ["{static}", "data:", "https://*.stripe.com"],
+ 'img-src': ["{static}", "{media}", "data:", "https://*.stripe.com"],
# form-action is not only used to match on form actions, but also on URLs
# form-actions redirect to. In the context of e.g. payment providers or
# single-sign-on this can be nearly anything so we cannot really restrict
@@ -193,6 +197,9 @@ class SecurityMiddleware(MiddlewareMixin):
staticdomain = "'self'"
dynamicdomain = "'self'"
+ mediadomain = "'self'"
+ if settings.MEDIA_URL.startswith('http'):
+ mediadomain += " " + settings.MEDIA_URL[:settings.MEDIA_URL.find('/', 9)]
if settings.STATIC_URL.startswith('http'):
staticdomain += " " + settings.STATIC_URL[:settings.STATIC_URL.find('/', 9)]
if settings.SITE_URL.startswith('http'):
@@ -212,5 +219,6 @@ class SecurityMiddleware(MiddlewareMixin):
dynamicdomain += " " + domain
if request.path not in self.CSP_EXEMPT:
- resp['Content-Security-Policy'] = _render_csp(h).format(static=staticdomain, dynamic=dynamicdomain)
+ resp['Content-Security-Policy'] = _render_csp(h).format(static=staticdomain, dynamic=dynamicdomain,
+ media=mediadomain, nonce=request.csp_nonce)
return resp
diff --git a/src/pretix/presale/style.py b/src/pretix/presale/style.py
index cbb110d4f..7d3639b2a 100644
--- a/src/pretix/presale/style.py
+++ b/src/pretix/presale/style.py
@@ -1,16 +1,19 @@
import hashlib
import logging
import os
+from urllib.parse import urljoin, urlsplit
import django_libsass
import sass
from django.conf import settings
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
+from django.templatetags.static import static as _static
from pretix.base.models import Event
from pretix.base.services.async import ProfiledTask
from pretix.celery_app import app
+from pretix.multidomain.urlreverse import get_domain
logger = logging.getLogger('pretix.presale.style')
@@ -25,10 +28,25 @@ def regenerate_css(event_id: int):
sassrules.append('$brand-primary: {};'.format(event.settings.get('primary_color')))
sassrules.append('@import "main.scss";')
+ def static(path):
+ sp = _static(path)
+ if not settings.MEDIA_URL.startswith("/") and sp.startswith("/"):
+ domain = get_domain(event.organizer)
+ if domain:
+ siteurlsplit = urlsplit(settings.SITE_URL)
+ if siteurlsplit.port and siteurlsplit.port not in (80, 443):
+ domain = '%s:%d' % (domain, siteurlsplit.port)
+ sp = urljoin('%s://%s' % (siteurlsplit.scheme, domain), sp)
+ else:
+ sp = urljoin(settings.SITE_URL, sp)
+ return '"{}"'.format(sp)
+
+ cf = dict(django_libsass.CUSTOM_FUNCTIONS)
+ cf['static'] = static
css = sass.compile(
string="\n".join(sassrules),
include_paths=[sassdir], output_style='compressed',
- custom_functions=django_libsass.CUSTOM_FUNCTIONS
+ custom_functions=cf
)
checksum = hashlib.sha1(css.encode('utf-8')).hexdigest()
fname = '{}/{}/presale.{}.css'.format(
diff --git a/src/pretix/presale/templates/pretixpresale/base.html b/src/pretix/presale/templates/pretixpresale/base.html
index 669ee7aed..23b6b2e57 100644
--- a/src/pretix/presale/templates/pretixpresale/base.html
+++ b/src/pretix/presale/templates/pretixpresale/base.html
@@ -11,7 +11,7 @@
{% endcompress %}
{% if css_file %}
-
+
{% else %}
{% compress css %}