Removed Django 1.1 fallback for CSRF checks.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@15948 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Luke Plant 2011-03-30 17:34:14 +00:00
parent 4550f95f29
commit 21ef64e34c
2 changed files with 26 additions and 99 deletions

View File

@ -34,7 +34,6 @@ _MAX_CSRF_KEY = 18446744073709551616L # 2 << 63
REASON_NO_REFERER = "Referer checking failed - no Referer." REASON_NO_REFERER = "Referer checking failed - no Referer."
REASON_BAD_REFERER = "Referer checking failed - %s does not match %s." REASON_BAD_REFERER = "Referer checking failed - %s does not match %s."
REASON_NO_COOKIE = "No CSRF or session cookie."
REASON_NO_CSRF_COOKIE = "CSRF cookie not set." REASON_NO_CSRF_COOKIE = "CSRF cookie not set."
REASON_BAD_TOKEN = "CSRF token missing or incorrect." REASON_BAD_TOKEN = "CSRF token missing or incorrect."
@ -105,22 +104,14 @@ class CsrfViewMiddleware(object):
if getattr(request, 'csrf_processing_done', False): if getattr(request, 'csrf_processing_done', False):
return None return None
# If the user doesn't have a CSRF cookie, generate one and store it in the
# request, so it's available to the view. We'll store it in a cookie when
# we reach the response.
try: try:
# In case of cookies from untrusted sources, we strip anything csrf_token = _sanitize_token(request.COOKIES[settings.CSRF_COOKIE_NAME])
# dangerous at this point, so that the cookie + token will have the # Use same token next time
# same, sanitized value. request.META['CSRF_COOKIE'] = csrf_token
request.META["CSRF_COOKIE"] = _sanitize_token(request.COOKIES[settings.CSRF_COOKIE_NAME])
cookie_is_new = False
except KeyError: except KeyError:
# No cookie, so create one. This will be sent with the next csrf_token = None
# response. # Generate token and store it in the request, so it's available to the view.
request.META["CSRF_COOKIE"] = _get_new_csrf_key() request.META["CSRF_COOKIE"] = _get_new_csrf_key()
# Set a flag to allow us to fall back and allow the session id in
# place of a CSRF cookie for this request only.
cookie_is_new = True
# Wait until request.META["CSRF_COOKIE"] has been manipulated before # Wait until request.META["CSRF_COOKIE"] has been manipulated before
# bailing out, so that get_token still works # bailing out, so that get_token still works
@ -173,27 +164,17 @@ class CsrfViewMiddleware(object):
) )
return self._reject(request, reason) return self._reject(request, reason)
# If the user didn't already have a CSRF cookie, then fall back to if csrf_token is None:
# the Django 1.1 method (hash of session ID), so a request is not # No CSRF cookie. For POST requests, we insist on a CSRF cookie,
# rejected if the form was sent to the user before upgrading to the # and in this way we can avoid all CSRF attacks, including login
# Django 1.2 method (session independent nonce) # CSRF.
if cookie_is_new: logger.warning('Forbidden (%s): %s' % (REASON_NO_CSRF_COOKIE, request.path),
try: extra={
session_id = request.COOKIES[settings.SESSION_COOKIE_NAME] 'status_code': 403,
csrf_token = _make_legacy_session_token(session_id) 'request': request,
except KeyError: }
# No CSRF cookie and no session cookie. For POST requests, )
# we insist on a CSRF cookie, and in this way we can avoid return self._reject(request, REASON_NO_CSRF_COOKIE)
# all CSRF attacks, including login CSRF.
logger.warning('Forbidden (%s): %s' % (REASON_NO_COOKIE, request.path),
extra={
'status_code': 403,
'request': request,
}
)
return self._reject(request, REASON_NO_COOKIE)
else:
csrf_token = request.META["CSRF_COOKIE"]
# check incoming token # check incoming token
request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
@ -202,23 +183,13 @@ class CsrfViewMiddleware(object):
request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '') request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '')
if not constant_time_compare(request_csrf_token, csrf_token): if not constant_time_compare(request_csrf_token, csrf_token):
if cookie_is_new: logger.warning('Forbidden (%s): %s' % (REASON_BAD_TOKEN, request.path),
# probably a problem setting the CSRF cookie extra={
logger.warning('Forbidden (%s): %s' % (REASON_NO_CSRF_COOKIE, request.path), 'status_code': 403,
extra={ 'request': request,
'status_code': 403, }
'request': request, )
} return self._reject(request, REASON_BAD_TOKEN)
)
return self._reject(request, REASON_NO_CSRF_COOKIE)
else:
logger.warning('Forbidden (%s): %s' % (REASON_BAD_TOKEN, request.path),
extra={
'status_code': 403,
'request': request,
}
)
return self._reject(request, REASON_BAD_TOKEN)
return self._accept(request) return self._accept(request)

View File

@ -6,8 +6,6 @@ from django.http import HttpRequest, HttpResponse
from django.middleware.csrf import CsrfMiddleware, CsrfViewMiddleware from django.middleware.csrf import CsrfMiddleware, CsrfViewMiddleware
from django.views.decorators.csrf import csrf_exempt, csrf_view_exempt, requires_csrf_token from django.views.decorators.csrf import csrf_exempt, csrf_view_exempt, requires_csrf_token
from django.core.context_processors import csrf from django.core.context_processors import csrf
from django.contrib.sessions.middleware import SessionMiddleware
from django.utils.importlib import import_module
from django.conf import settings from django.conf import settings
from django.template import RequestContext, Template from django.template import RequestContext, Template
@ -62,14 +60,6 @@ class CsrfMiddlewareTest(TestCase):
_csrf_id_cookie = "<1>\xc2\xa1" _csrf_id_cookie = "<1>\xc2\xa1"
_csrf_id = "1" _csrf_id = "1"
# This is a valid session token for this ID and secret key. This was generated using
# the old code that we're to be backwards-compatible with. Don't use the CSRF code
# to generate this hash, or we're merely testing the code against itself and not
# checking backwards-compatibility. This is also the output of (echo -n test1 | md5sum).
_session_token = "5a105e8b9d40e1329780d62ea2265d8a"
_session_id = "1"
_secret_key_for_session_test= "test"
def setUp(self): def setUp(self):
self.save_warnings_state() self.save_warnings_state()
warnings.filterwarnings('ignore', category=DeprecationWarning, warnings.filterwarnings('ignore', category=DeprecationWarning,
@ -101,17 +91,6 @@ class CsrfMiddlewareTest(TestCase):
req.POST['csrfmiddlewaretoken'] = self._csrf_id req.POST['csrfmiddlewaretoken'] = self._csrf_id
return req return req
def _get_POST_session_request_with_token(self):
req = self._get_POST_no_csrf_cookie_request()
req.COOKIES[settings.SESSION_COOKIE_NAME] = self._session_id
req.POST['csrfmiddlewaretoken'] = self._session_token
return req
def _get_POST_session_request_no_token(self):
req = self._get_POST_no_csrf_cookie_request()
req.COOKIES[settings.SESSION_COOKIE_NAME] = self._session_id
return req
def _check_token_present(self, response, csrf_id=None): def _check_token_present(self, response, csrf_id=None):
self.assertContains(response, "name='csrfmiddlewaretoken' value='%s'" % (csrf_id or self._csrf_id)) self.assertContains(response, "name='csrfmiddlewaretoken' value='%s'" % (csrf_id or self._csrf_id))
@ -226,10 +205,10 @@ class CsrfMiddlewareTest(TestCase):
self.assertEqual(resp_content, resp2.content) self.assertEqual(resp_content, resp2.content)
# Check the request processing # Check the request processing
def test_process_request_no_session_no_csrf_cookie(self): def test_process_request_no_csrf_cookie(self):
""" """
Check that if neither a CSRF cookie nor a session cookie are present, Check that if no CSRF cookies is present, the middleware rejects the
the middleware rejects the incoming request. This will stop login CSRF. incoming request. This will stop login CSRF.
""" """
req = self._get_POST_no_csrf_cookie_request() req = self._get_POST_no_csrf_cookie_request()
req2 = CsrfMiddleware().process_view(req, post_form_view, (), {}) req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
@ -252,29 +231,6 @@ class CsrfMiddlewareTest(TestCase):
req2 = CsrfMiddleware().process_view(req, post_form_view, (), {}) req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
self.assertEqual(None, req2) self.assertEqual(None, req2)
def test_process_request_session_cookie_no_csrf_cookie_token(self):
"""
When no CSRF cookie exists, but the user has a session, check that a token
using the session cookie as a legacy CSRF cookie is accepted.
"""
orig_secret_key = settings.SECRET_KEY
settings.SECRET_KEY = self._secret_key_for_session_test
try:
req = self._get_POST_session_request_with_token()
req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
self.assertEqual(None, req2)
finally:
settings.SECRET_KEY = orig_secret_key
def test_process_request_session_cookie_no_csrf_cookie_no_token(self):
"""
Check that if a session cookie is present but no token and no CSRF cookie,
the request is rejected.
"""
req = self._get_POST_session_request_no_token()
req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
self.assertEqual(403, req2.status_code)
def test_process_request_csrf_cookie_no_token_exempt_view(self): def test_process_request_csrf_cookie_no_token_exempt_view(self):
""" """
Check that if a CSRF cookie is present and no token, but the csrf_exempt Check that if a CSRF cookie is present and no token, but the csrf_exempt