summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Godwin <andrew@aeracode.org>2013-05-23 16:14:27 +0100
committerAndrew Godwin <andrew@aeracode.org>2013-05-24 22:15:08 +0100
commit1514f17aa60772f48839130f9a5071b9ffe15213 (patch)
tree11ba74b064ef6a86c0528ee0ffd77d548fa61e10
parent7e95d7a93088675f984d1079e4e3c3ddc1723b0f (diff)
Rotate CSRF token on login
-rw-r--r--django/contrib/auth/__init__.py2
-rw-r--r--django/contrib/auth/tests/test_views.py40
-rw-r--r--django/middleware/csrf.py8
3 files changed, 49 insertions, 1 deletions
diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py
index e032038775..029193d582 100644
--- a/django/contrib/auth/__init__.py
+++ b/django/contrib/auth/__init__.py
@@ -3,6 +3,7 @@ import re
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.utils.module_loading import import_by_path
+from django.middleware.csrf import rotate_token
from .signals import user_logged_in, user_logged_out, user_login_failed
@@ -84,6 +85,7 @@ def login(request, user):
request.session[BACKEND_SESSION_KEY] = user.backend
if hasattr(request, 'user'):
request.user = user
+ rotate_token(request)
user_logged_in.send(sender=user.__class__, request=request, user=user)
diff --git a/django/contrib/auth/tests/test_views.py b/django/contrib/auth/tests/test_views.py
index a6bea47877..fe1d7fb52f 100644
--- a/django/contrib/auth/tests/test_views.py
+++ b/django/contrib/auth/tests/test_views.py
@@ -12,18 +12,21 @@ from django.contrib.auth.models import User
from django.core import mail
from django.core.exceptions import SuspiciousOperation
from django.core.urlresolvers import reverse, NoReverseMatch
-from django.http import QueryDict
+from django.http import QueryDict, HttpRequest
from django.utils.encoding import force_text
from django.utils.html import escape
from django.utils.http import urlquote
from django.utils._os import upath
from django.test import TestCase
from django.test.utils import override_settings
+from django.middleware.csrf import CsrfViewMiddleware
+from django.contrib.sessions.middleware import SessionMiddleware
from django.contrib.auth import SESSION_KEY, REDIRECT_FIELD_NAME
from django.contrib.auth.forms import (AuthenticationForm, PasswordChangeForm,
SetPasswordForm, PasswordResetForm)
from django.contrib.auth.tests.utils import skipIfCustomUser
+from django.contrib.auth.views import login as login_view
@override_settings(
@@ -460,6 +463,41 @@ class LoginTest(AuthViewsTestCase):
# the custom authentication form used by this login asserts
# that a request is passed to the form successfully.
+ def test_login_csrf_rotate(self, password='password'):
+ """
+ Makes sure that a login rotates the currently-used CSRF token.
+ """
+ # Do a GET to establish a CSRF token
+ # TestClient isn't used here as we're testing middleware, essentially.
+ req = HttpRequest()
+ CsrfViewMiddleware().process_view(req, login_view, (), {})
+ req.META["CSRF_COOKIE_USED"] = True
+ resp = login_view(req)
+ resp2 = CsrfViewMiddleware().process_response(req, resp)
+ csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, None)
+ token1 = csrf_cookie.coded_value
+
+ # Prepare the POST request
+ req = HttpRequest()
+ req.COOKIES[settings.CSRF_COOKIE_NAME] = token1
+ req.method = "POST"
+ req.POST = {'username': 'testclient', 'password': password, 'csrfmiddlewaretoken': token1}
+ req.REQUEST = req.POST
+
+ # Use POST request to log in
+ SessionMiddleware().process_request(req)
+ CsrfViewMiddleware().process_view(req, login_view, (), {})
+ req.META["SERVER_NAME"] = "testserver" # Required to have redirect work in login view
+ req.META["SERVER_PORT"] = 80
+ req.META["CSRF_COOKIE_USED"] = True
+ resp = login_view(req)
+ resp2 = CsrfViewMiddleware().process_response(req, resp)
+ csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, None)
+ token2 = csrf_cookie.coded_value
+
+ # Check the CSRF token switched
+ self.assertNotEqual(token1, token2)
+
@skipIfCustomUser
class LoginURLSettings(AuthViewsTestCase):
diff --git a/django/middleware/csrf.py b/django/middleware/csrf.py
index 98974f011a..1b5732fbbf 100644
--- a/django/middleware/csrf.py
+++ b/django/middleware/csrf.py
@@ -53,6 +53,14 @@ def get_token(request):
return request.META.get("CSRF_COOKIE", None)
+def rotate_token(request):
+ """
+ Changes the CSRF token in use for a request - should be done on login
+ for security purposes.
+ """
+ request.META["CSRF_COOKIE"] = _get_new_csrf_key()
+
+
def _sanitize_token(token):
# Allow only alphanum
if len(token) > CSRF_KEY_LENGTH: