mirror of
https://github.com/django/django.git
synced 2025-01-03 06:55:47 +00:00
Fixed #25334 -- Provided a way to allow cross-origin unsafe requests over HTTPS.
Added the CSRF_TRUSTED_ORIGINS setting which contains a list of other domains that are included during the CSRF Referer header verification for secure (HTTPS) requests.
This commit is contained in:
parent
48c420d992
commit
ab26b65b2f
@ -558,6 +558,7 @@ CSRF_COOKIE_PATH = '/'
|
|||||||
CSRF_COOKIE_SECURE = False
|
CSRF_COOKIE_SECURE = False
|
||||||
CSRF_COOKIE_HTTPONLY = False
|
CSRF_COOKIE_HTTPONLY = False
|
||||||
CSRF_HEADER_NAME = 'HTTP_X_CSRFTOKEN'
|
CSRF_HEADER_NAME = 'HTTP_X_CSRFTOKEN'
|
||||||
|
CSRF_TRUSTED_ORIGINS = []
|
||||||
|
|
||||||
############
|
############
|
||||||
# MESSAGES #
|
# MESSAGES #
|
||||||
|
@ -19,7 +19,7 @@ from django.utils.http import same_origin
|
|||||||
logger = logging.getLogger('django.request')
|
logger = logging.getLogger('django.request')
|
||||||
|
|
||||||
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 any trusted origins."
|
||||||
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."
|
||||||
|
|
||||||
@ -154,10 +154,15 @@ class CsrfViewMiddleware(object):
|
|||||||
if referer is None:
|
if referer is None:
|
||||||
return self._reject(request, REASON_NO_REFERER)
|
return self._reject(request, REASON_NO_REFERER)
|
||||||
|
|
||||||
|
# Here we generate a list of all acceptable HTTP referers,
|
||||||
|
# including the current host since that has been validated
|
||||||
|
# upstream.
|
||||||
|
good_hosts = list(settings.CSRF_TRUSTED_ORIGINS)
|
||||||
# Note that request.get_host() includes the port.
|
# Note that request.get_host() includes the port.
|
||||||
good_referer = 'https://%s/' % request.get_host()
|
good_hosts.append(request.get_host())
|
||||||
if not same_origin(referer, good_referer):
|
good_referers = ['https://{0}/'.format(host) for host in good_hosts]
|
||||||
reason = REASON_BAD_REFERER % (referer, good_referer)
|
if not any(same_origin(referer, host) for host in good_referers):
|
||||||
|
reason = REASON_BAD_REFERER % referer
|
||||||
return self._reject(request, reason)
|
return self._reject(request, reason)
|
||||||
|
|
||||||
if csrf_token is None:
|
if csrf_token is None:
|
||||||
|
@ -257,7 +257,8 @@ The CSRF protection is based on the following things:
|
|||||||
due to the fact that HTTP 'Set-Cookie' headers are (unfortunately) accepted
|
due to the fact that HTTP 'Set-Cookie' headers are (unfortunately) accepted
|
||||||
by clients that are talking to a site under HTTPS. (Referer checking is not
|
by clients that are talking to a site under HTTPS. (Referer checking is not
|
||||||
done for HTTP requests because the presence of the Referer header is not
|
done for HTTP requests because the presence of the Referer header is not
|
||||||
reliable enough under HTTP.)
|
reliable enough under HTTP.) Expanding the accepted referers beyond the
|
||||||
|
current host can be done with the :setting:`CSRF_TRUSTED_ORIGINS` setting.
|
||||||
|
|
||||||
This ensures that only forms that have originated from your Web site can be used
|
This ensures that only forms that have originated from your Web site can be used
|
||||||
to POST data back.
|
to POST data back.
|
||||||
@ -460,3 +461,4 @@ A number of settings can be used to control Django's CSRF behavior:
|
|||||||
* :setting:`CSRF_COOKIE_SECURE`
|
* :setting:`CSRF_COOKIE_SECURE`
|
||||||
* :setting:`CSRF_FAILURE_VIEW`
|
* :setting:`CSRF_FAILURE_VIEW`
|
||||||
* :setting:`CSRF_HEADER_NAME`
|
* :setting:`CSRF_HEADER_NAME`
|
||||||
|
* :setting:`CSRF_TRUSTED_ORIGINS`
|
||||||
|
@ -428,6 +428,23 @@ 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
|
For example, if your client sends a ``'X-XSRF-TOKEN'`` header, the setting
|
||||||
should be ``'HTTP_X_XSRF_TOKEN'``.
|
should be ``'HTTP_X_XSRF_TOKEN'``.
|
||||||
|
|
||||||
|
.. setting:: CSRF_TRUSTED_ORIGINS
|
||||||
|
|
||||||
|
CSRF_TRUSTED_ORIGINS
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
.. versionadded:: 1.9
|
||||||
|
|
||||||
|
Default: ``[]`` (Empty list)
|
||||||
|
|
||||||
|
A list of hosts which are trusted origins for unsafe requests (e.g. ``POST``).
|
||||||
|
For a :meth:`secure <django.http.HttpRequest.is_secure>` unsafe
|
||||||
|
request, Django's CSRF protection requires that the request have a ``Referer``
|
||||||
|
header that matches the origin present in the ``Host`` header. This prevents,
|
||||||
|
for example, a ``POST`` request from ``subdomain.example.com`` from succeeding
|
||||||
|
against ``api.example.com``. If you need cross-origin unsafe requests over
|
||||||
|
HTTPS, continuing the example, add ``"subdomain.example.com"`` to this list.
|
||||||
|
|
||||||
.. setting:: DATABASES
|
.. setting:: DATABASES
|
||||||
|
|
||||||
DATABASES
|
DATABASES
|
||||||
@ -3374,6 +3391,7 @@ Security
|
|||||||
* :setting:`CSRF_COOKIE_SECURE`
|
* :setting:`CSRF_COOKIE_SECURE`
|
||||||
* :setting:`CSRF_FAILURE_VIEW`
|
* :setting:`CSRF_FAILURE_VIEW`
|
||||||
* :setting:`CSRF_HEADER_NAME`
|
* :setting:`CSRF_HEADER_NAME`
|
||||||
|
* :setting:`CSRF_TRUSTED_ORIGINS`
|
||||||
|
|
||||||
* :setting:`SECRET_KEY`
|
* :setting:`SECRET_KEY`
|
||||||
* :setting:`X_FRAME_OPTIONS`
|
* :setting:`X_FRAME_OPTIONS`
|
||||||
|
@ -484,6 +484,9 @@ CSRF
|
|||||||
* The request header's name used for CSRF authentication can be customized
|
* The request header's name used for CSRF authentication can be customized
|
||||||
with :setting:`CSRF_HEADER_NAME`.
|
with :setting:`CSRF_HEADER_NAME`.
|
||||||
|
|
||||||
|
* The new :setting:`CSRF_TRUSTED_ORIGINS` setting provides a way to allow
|
||||||
|
cross-origin unsafe requests (e.g. ``POST``) over HTTPS.
|
||||||
|
|
||||||
Signals
|
Signals
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
|
|
||||||
|
@ -644,6 +644,7 @@ refactoring
|
|||||||
refactorings
|
refactorings
|
||||||
refactors
|
refactors
|
||||||
referer
|
referer
|
||||||
|
referers
|
||||||
reflow
|
reflow
|
||||||
regex
|
regex
|
||||||
regexes
|
regexes
|
||||||
|
@ -352,6 +352,19 @@ class CsrfViewMiddlewareTest(SimpleTestCase):
|
|||||||
req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
|
req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
|
||||||
self.assertIsNone(req2)
|
self.assertIsNone(req2)
|
||||||
|
|
||||||
|
@override_settings(ALLOWED_HOSTS=['www.example.com'], CSRF_TRUSTED_ORIGINS=['dashboard.example.com'])
|
||||||
|
def test_https_csrf_trusted_origin_allowed(self):
|
||||||
|
"""
|
||||||
|
A POST HTTPS request with a referer added to the CSRF_TRUSTED_ORIGINS
|
||||||
|
setting is accepted.
|
||||||
|
"""
|
||||||
|
req = self._get_POST_request_with_token()
|
||||||
|
req._is_secure_override = True
|
||||||
|
req.META['HTTP_HOST'] = 'www.example.com'
|
||||||
|
req.META['HTTP_REFERER'] = 'https://dashboard.example.com'
|
||||||
|
req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
|
||||||
|
self.assertIsNone(req2)
|
||||||
|
|
||||||
def test_ensures_csrf_cookie_no_middleware(self):
|
def test_ensures_csrf_cookie_no_middleware(self):
|
||||||
"""
|
"""
|
||||||
Tests that ensures_csrf_cookie decorator fulfils its promise
|
Tests that ensures_csrf_cookie decorator fulfils its promise
|
||||||
|
Loading…
Reference in New Issue
Block a user