summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
Diffstat (limited to 'django')
-rw-r--r--django/conf/global_settings.py1
-rw-r--r--django/middleware/csrf.py79
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