diff --git a/AUTHORS b/AUTHORS index b148b5409c4..333787d12f3 100644 --- a/AUTHORS +++ b/AUTHORS @@ -264,6 +264,7 @@ answer newbie questions, and generally made Django that much better: Greg Chapple Gregor Müllegger Grigory Fateyev + Grzegorz Ślusarek Guilherme Mesquita Gondim Guillaume Pannatier Gustavo Picon diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 41abd4f499e..d817ea8e944 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -555,6 +555,7 @@ CSRF_COOKIE_DOMAIN = None CSRF_COOKIE_PATH = '/' CSRF_COOKIE_SECURE = False CSRF_COOKIE_HTTPONLY = False +CSRF_HEADER_NAME = 'HTTP_X_CSRFTOKEN' ############ # MESSAGES # diff --git a/django/middleware/csrf.py b/django/middleware/csrf.py index f12126fa9f6..8891a441090 100644 --- a/django/middleware/csrf.py +++ b/django/middleware/csrf.py @@ -183,7 +183,7 @@ class CsrfViewMiddleware(object): if request_csrf_token == "": # Fall back to X-CSRFToken, to make things easier for AJAX, # and possible for PUT/DELETE. - request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '') + request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '') if not constant_time_compare(request_csrf_token, csrf_token): return self._reject(request, REASON_BAD_TOKEN) diff --git a/docs/ref/csrf.txt b/docs/ref/csrf.txt index 4eaf7103543..fbed9739524 100644 --- a/docs/ref/csrf.txt +++ b/docs/ref/csrf.txt @@ -92,6 +92,9 @@ protection for your views as outlined above. The CSRF token cookie is named ``csrftoken`` by default, but you can control the cookie name via the :setting:`CSRF_COOKIE_NAME` setting. + The CSRF header name is ``HTTP_X_CSRFTOKEN`` by default, but you can + customize it using the :setting:`CSRF_HEADER_NAME` setting. + Acquiring the token is straightforward: .. code-block:: javascript @@ -456,3 +459,4 @@ A number of settings can be used to control Django's CSRF behavior: * :setting:`CSRF_COOKIE_PATH` * :setting:`CSRF_COOKIE_SECURE` * :setting:`CSRF_FAILURE_VIEW` +* :setting:`CSRF_HEADER_NAME` diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 6db21f179d0..497f3215318 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -409,6 +409,23 @@ where ``reason`` is a short message (intended for developers or logging, not for end users) indicating the reason the request was rejected. See :doc:`/ref/csrf`. +.. setting:: CSRF_HEADER_NAME + +CSRF_HEADER_NAME +---------------- + +.. versionadded:: 1.9 + +Default: ``'HTTP_X_CSRFTOKEN'`` + +The name of the request header used for CSRF authentication. + +As with other HTTP headers in ``request.META``, the header name received from +the server is normalized by converting all characters to uppercase, replacing +any hyphens with underscores, and adding an ``'HTTP_'`` prefix to the name. +For example, if your client sends a ``'X-XSRF-TOKEN'`` header, the setting +should be ``'HTTP_X_XSRF_TOKEN'``. + .. setting:: DATABASES DATABASES @@ -3261,6 +3278,7 @@ Security * :setting:`CSRF_COOKIE_PATH` * :setting:`CSRF_COOKIE_SECURE` * :setting:`CSRF_FAILURE_VIEW` + * :setting:`CSRF_HEADER_NAME` * :setting:`SECRET_KEY` * :setting:`X_FRAME_OPTIONS` diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt index 792fdced226..4cfdaaa1ebc 100644 --- a/docs/releases/1.9.txt +++ b/docs/releases/1.9.txt @@ -142,6 +142,12 @@ Models managers created by ``ForeignKey``, ``GenericForeignKey``, and ``ManyToManyField``. +CSRF +^^^^ + +* The request header's name used for CSRF authentication can be customized + with :setting:`CSRF_HEADER_NAME`. + Signals ^^^^^^^ diff --git a/tests/csrf_tests/tests.py b/tests/csrf_tests/tests.py index 21caacc1d9d..7469da34b27 100644 --- a/tests/csrf_tests/tests.py +++ b/tests/csrf_tests/tests.py @@ -189,6 +189,16 @@ class CsrfViewMiddlewareTest(TestCase): req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {}) self.assertIsNone(req2) + @override_settings(CSRF_HEADER_NAME='HTTP_X_CSRFTOKEN_CUSTOMIZED') + def test_csrf_token_in_header_with_customized_name(self): + """ + settings.CSRF_HEADER_NAME can be used to customize the CSRF header name + """ + req = self._get_POST_csrf_cookie_request() + req.META['HTTP_X_CSRFTOKEN_CUSTOMIZED'] = self._csrf_id + req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {}) + self.assertIsNone(req2) + def test_put_and_delete_rejected(self): """ Tests that HTTP PUT and DELETE methods have protection