diff --git a/src/pretix/base/views/errors.py b/src/pretix/base/views/errors.py index 7b76973b9b..dbc92e20a1 100644 --- a/src/pretix/base/views/errors.py +++ b/src/pretix/base/views/errors.py @@ -27,11 +27,11 @@ from django.template import TemplateDoesNotExist, loader from django.template.loader import get_template from django.utils.functional import Promise from django.utils.translation import gettext as _ -from django.views.decorators.csrf import requires_csrf_token from sentry_sdk import last_event_id from pretix.base.i18n import language from pretix.base.middleware import get_language_from_request +from pretix.multidomain.middlewares import requires_csrf_token def csrf_failure(request, reason=""): diff --git a/src/pretix/multidomain/middlewares.py b/src/pretix/multidomain/middlewares.py index 8207270af2..114341df7d 100644 --- a/src/pretix/multidomain/middlewares.py +++ b/src/pretix/multidomain/middlewares.py @@ -50,6 +50,7 @@ from django.middleware.csrf import ( from django.shortcuts import render from django.urls import set_urlconf from django.utils.cache import patch_vary_headers +from django.utils.decorators import decorator_from_middleware from django.utils.deprecation import MiddlewareMixin from django.utils.http import http_date from django_scopes import scopes_disabled @@ -270,26 +271,50 @@ class CsrfViewMiddleware(BaseCsrfMiddleware): if request.session.get(CSRF_SESSION_KEY) != request.META["CSRF_COOKIE"]: request.session[CSRF_SESSION_KEY] = request.META["CSRF_COOKIE"] else: - is_secure = request.scheme == 'https' # Set the CSRF cookie even if it's already set, so we renew # the expiry timer. - if is_secure and settings.CSRF_COOKIE_NAME in request.COOKIES: # remove legacy cookie + if request.is_secure() and settings.CSRF_COOKIE_NAME in request.COOKIES: # remove legacy cookie # response.delete_cookie does not work as we might have set a partitioned cookie delete_cookie_without_samesite( request, response, settings.CSRF_COOKIE_NAME, path=settings.CSRF_COOKIE_PATH, - secure=is_secure, + secure=request.is_secure(), httponly=settings.CSRF_COOKIE_HTTPONLY ) set_cookie_without_samesite( request, response, - '__Host-' + settings.CSRF_COOKIE_NAME if is_secure else settings.CSRF_COOKIE_NAME, + '__Host-' + settings.CSRF_COOKIE_NAME if request.is_secure() else settings.CSRF_COOKIE_NAME, request.META["CSRF_COOKIE"], max_age=settings.CSRF_COOKIE_AGE, path=settings.CSRF_COOKIE_PATH, - secure=is_secure, + secure=request.is_secure(), httponly=settings.CSRF_COOKIE_HTTPONLY ) # Content varies with the CSRF cookie, so set the Vary header. patch_vary_headers(response, ('Cookie',)) + + def process_response(self, request, response): + if ( + not settings.CSRF_USE_SESSIONS + and request.is_secure() + and settings.CSRF_COOKIE_NAME in response.cookies + and response.cookies[settings.CSRF_COOKIE_NAME].value + ): + raise ValueError("Usage of djangos CsrfViewMiddleware detected (legacy cookie found in response). " + "This may be caused by using csrf_project or requires_csrf_token from django.views.decorators.csrf. " + "Use the pretix.multidomain.middlewares equivalents instead.") + + return super().process_response(request, response) + + +csrf_protect = decorator_from_middleware(CsrfViewMiddleware) + + +class _EnsureCsrfToken(CsrfViewMiddleware): + # Behave like CsrfViewMiddleware but don't reject requests or log warnings. + def _reject(self, request, reason): + return None + + +requires_csrf_token = decorator_from_middleware(_EnsureCsrfToken) diff --git a/src/pretix/presale/views/customer.py b/src/pretix/presale/views/customer.py index 0919bea236..2696e85839 100644 --- a/src/pretix/presale/views/customer.py +++ b/src/pretix/presale/views/customer.py @@ -42,7 +42,6 @@ from django.utils.functional import cached_property from django.utils.http import url_has_allowed_host_and_scheme from django.utils.translation import gettext_lazy as _ from django.views.decorators.cache import never_cache -from django.views.decorators.csrf import csrf_protect from django.views.decorators.debug import sensitive_post_parameters from django.views.generic import FormView, ListView, View @@ -55,6 +54,7 @@ from pretix.base.settings import PERSON_NAME_SCHEMES from pretix.base.signals import customer_created, customer_signed_in from pretix.helpers.compat import CompatDeleteView from pretix.helpers.http import redirect_to_url +from pretix.multidomain.middlewares import csrf_protect from pretix.multidomain.models import KnownDomain from pretix.multidomain.urlreverse import build_absolute_uri, eventreverse from pretix.presale.forms.customer import (