diff options
Diffstat (limited to 'django')
| -rw-r--r-- | django/conf/global_settings.py | 1 | ||||
| -rw-r--r-- | django/middleware/csrf.py | 79 |
2 files changed, 55 insertions, 25 deletions
diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index b123c1136b..206d66f15d 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -548,6 +548,7 @@ CSRF_COOKIE_SECURE = False CSRF_COOKIE_HTTPONLY = False CSRF_HEADER_NAME = 'HTTP_X_CSRFTOKEN' CSRF_TRUSTED_ORIGINS = [] +CSRF_USE_SESSIONS = False ############ # MESSAGES # diff --git a/django/middleware/csrf.py b/django/middleware/csrf.py index 3f9a649c27..d7359e4912 100644 --- a/django/middleware/csrf.py +++ b/django/middleware/csrf.py @@ -11,6 +11,7 @@ import re import string from django.conf import settings +from django.core.exceptions import ImproperlyConfigured from django.urls import get_callable from django.utils.cache import patch_vary_headers from django.utils.crypto import constant_time_compare, get_random_string @@ -32,6 +33,7 @@ REASON_INSECURE_REFERER = "Referer checking failed - Referer is insecure while h CSRF_SECRET_LENGTH = 32 CSRF_TOKEN_LENGTH = 2 * CSRF_SECRET_LENGTH CSRF_ALLOWED_CHARS = string.ascii_letters + string.digits +CSRF_SESSION_KEY = '_csrftoken' def _get_failure_view(): @@ -160,20 +162,51 @@ class CsrfViewMiddleware(MiddlewareMixin): ) return _get_failure_view()(request, reason=reason) - def process_view(self, request, callback, callback_args, callback_kwargs): - if getattr(request, 'csrf_processing_done', False): - return None - - try: - cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME] - except KeyError: - csrf_token = None + def _get_token(self, request): + if settings.CSRF_USE_SESSIONS: + try: + return request.session.get(CSRF_SESSION_KEY) + except AttributeError: + raise ImproperlyConfigured( + 'CSRF_USE_SESSIONS is enabled, but request.session is not ' + 'set. SessionMiddleware must appear before CsrfViewMiddleware ' + 'in MIDDLEWARE%s.' % ('_CLASSES' if settings.MIDDLEWARE is None else '') + ) else: + try: + cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME] + except KeyError: + return None + csrf_token = _sanitize_token(cookie_token) if csrf_token != cookie_token: # Cookie token needed to be replaced; # the cookie needs to be reset. request.csrf_cookie_needs_reset = True + return csrf_token + + def _set_token(self, request, response): + if settings.CSRF_USE_SESSIONS: + request.session[CSRF_SESSION_KEY] = request.META['CSRF_COOKIE'] + else: + response.set_cookie( + settings.CSRF_COOKIE_NAME, + request.META['CSRF_COOKIE'], + max_age=settings.CSRF_COOKIE_AGE, + domain=settings.CSRF_COOKIE_DOMAIN, + path=settings.CSRF_COOKIE_PATH, + secure=settings.CSRF_COOKIE_SECURE, + httponly=settings.CSRF_COOKIE_HTTPONLY, + ) + # Set the Vary header since content varies with the CSRF cookie. + patch_vary_headers(response, ('Cookie',)) + + def process_view(self, request, callback, callback_args, callback_kwargs): + if getattr(request, 'csrf_processing_done', False): + return None + + csrf_token = self._get_token(request) + if csrf_token is not None: # Use same token next time. request.META['CSRF_COOKIE'] = csrf_token @@ -226,16 +259,21 @@ class CsrfViewMiddleware(MiddlewareMixin): if referer.scheme != 'https': return self._reject(request, REASON_INSECURE_REFERER) - # If there isn't a CSRF_COOKIE_DOMAIN, assume we need an exact - # match on host:port. If not, obey the cookie rules. - if settings.CSRF_COOKIE_DOMAIN is None: - # request.get_host() includes the port. - good_referer = request.get_host() - else: - good_referer = settings.CSRF_COOKIE_DOMAIN + # If there isn't a CSRF_COOKIE_DOMAIN, require an exact match + # match on host:port. If not, obey the cookie rules (or those + # for the session cookie, if CSRF_USE_SESSIONS). + good_referer = ( + settings.SESSION_COOKIE_DOMAIN + if settings.CSRF_USE_SESSIONS + else settings.CSRF_COOKIE_DOMAIN + ) + if good_referer is not None: server_port = request.get_port() if server_port not in ('443', '80'): good_referer = '%s:%s' % (good_referer, server_port) + else: + # request.get_host() includes the port. + good_referer = request.get_host() # Here we generate a list of all acceptable HTTP referers, # including the current host since that has been validated @@ -287,15 +325,6 @@ class CsrfViewMiddleware(MiddlewareMixin): # Set the CSRF cookie even if it's already set, so we renew # the expiry timer. - response.set_cookie(settings.CSRF_COOKIE_NAME, - request.META["CSRF_COOKIE"], - max_age=settings.CSRF_COOKIE_AGE, - domain=settings.CSRF_COOKIE_DOMAIN, - path=settings.CSRF_COOKIE_PATH, - secure=settings.CSRF_COOKIE_SECURE, - httponly=settings.CSRF_COOKIE_HTTPONLY - ) - # Content varies with the CSRF cookie, so set the Vary header. - patch_vary_headers(response, ('Cookie',)) + self._set_token(request, response) response.csrf_cookie_set = True return response |
