mirror of https://github.com/django/django.git
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_HTTPONLY = False
|
||||
CSRF_HEADER_NAME = 'HTTP_X_CSRFTOKEN'
|
||||
CSRF_TRUSTED_ORIGINS = []
|
||||
|
||||
############
|
||||
# MESSAGES #
|
||||
|
|
|
@ -19,7 +19,7 @@ from django.utils.http import same_origin
|
|||
logger = logging.getLogger('django.request')
|
||||
|
||||
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_BAD_TOKEN = "CSRF token missing or incorrect."
|
||||
|
||||
|
@ -154,10 +154,15 @@ class CsrfViewMiddleware(object):
|
|||
if referer is None:
|
||||
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.
|
||||
good_referer = 'https://%s/' % request.get_host()
|
||||
if not same_origin(referer, good_referer):
|
||||
reason = REASON_BAD_REFERER % (referer, good_referer)
|
||||
good_hosts.append(request.get_host())
|
||||
good_referers = ['https://{0}/'.format(host) for host in good_hosts]
|
||||
if not any(same_origin(referer, host) for host in good_referers):
|
||||
reason = REASON_BAD_REFERER % referer
|
||||
return self._reject(request, reason)
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
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_FAILURE_VIEW`
|
||||
* :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
|
||||
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
|
||||
|
||||
DATABASES
|
||||
|
@ -3374,6 +3391,7 @@ Security
|
|||
* :setting:`CSRF_COOKIE_SECURE`
|
||||
* :setting:`CSRF_FAILURE_VIEW`
|
||||
* :setting:`CSRF_HEADER_NAME`
|
||||
* :setting:`CSRF_TRUSTED_ORIGINS`
|
||||
|
||||
* :setting:`SECRET_KEY`
|
||||
* :setting:`X_FRAME_OPTIONS`
|
||||
|
|
|
@ -484,6 +484,9 @@ CSRF
|
|||
* The request header's name used for CSRF authentication can be customized
|
||||
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
|
||||
^^^^^^^
|
||||
|
||||
|
|
|
@ -644,6 +644,7 @@ refactoring
|
|||
refactorings
|
||||
refactors
|
||||
referer
|
||||
referers
|
||||
reflow
|
||||
regex
|
||||
regexes
|
||||
|
|
|
@ -352,6 +352,19 @@ class CsrfViewMiddlewareTest(SimpleTestCase):
|
|||
req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
|
||||
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):
|
||||
"""
|
||||
Tests that ensures_csrf_cookie decorator fulfils its promise
|
||||
|
|
Loading…
Reference in New Issue