Refs #16010 -- Required CSRF_TRUSTED_ORIGINS setting to include the scheme.
This commit is contained in:
parent
9bf5e9418f
commit
dba44a7a7a
|
@ -7,6 +7,7 @@ from .registry import Tags, register, run_checks, tag_exists
|
|||
# Import these to force registration of checks
|
||||
import django.core.checks.async_checks # NOQA isort:skip
|
||||
import django.core.checks.caches # NOQA isort:skip
|
||||
import django.core.checks.compatibility.django_4_0 # NOQA isort:skip
|
||||
import django.core.checks.database # NOQA isort:skip
|
||||
import django.core.checks.files # NOQA isort:skip
|
||||
import django.core.checks.model_checks # NOQA isort:skip
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
from django.conf import settings
|
||||
|
||||
from .. import Error, Tags, register
|
||||
|
||||
|
||||
@register(Tags.compatibility)
|
||||
def check_csrf_trusted_origins(app_configs, **kwargs):
|
||||
errors = []
|
||||
for origin in settings.CSRF_TRUSTED_ORIGINS:
|
||||
if '://' not in origin:
|
||||
errors.append(Error(
|
||||
'As of Django 4.0, the values in the CSRF_TRUSTED_ORIGINS '
|
||||
'setting must start with a scheme (usually http:// or '
|
||||
'https://) but found %s. See the release notes for details.'
|
||||
% origin,
|
||||
id='4_0.E001',
|
||||
))
|
||||
return errors
|
|
@ -15,6 +15,7 @@ from django.urls import get_callable
|
|||
from django.utils.cache import patch_vary_headers
|
||||
from django.utils.crypto import constant_time_compare, get_random_string
|
||||
from django.utils.deprecation import MiddlewareMixin
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.http import is_same_domain
|
||||
from django.utils.log import log_response
|
||||
|
||||
|
@ -136,6 +137,13 @@ class CsrfViewMiddleware(MiddlewareMixin):
|
|||
This middleware should be used in conjunction with the {% csrf_token %}
|
||||
template tag.
|
||||
"""
|
||||
@cached_property
|
||||
def csrf_trusted_origins_hosts(self):
|
||||
return [
|
||||
urlparse(origin).netloc.lstrip('*')
|
||||
for origin in settings.CSRF_TRUSTED_ORIGINS
|
||||
]
|
||||
|
||||
# The _accept and _reject methods currently only exist for the sake of the
|
||||
# requires_csrf_token decorator.
|
||||
def _accept(self, request):
|
||||
|
@ -272,7 +280,7 @@ class CsrfViewMiddleware(MiddlewareMixin):
|
|||
|
||||
# Create a list of all acceptable HTTP referers, including the
|
||||
# current host if it's permitted by ALLOWED_HOSTS.
|
||||
good_hosts = list(settings.CSRF_TRUSTED_ORIGINS)
|
||||
good_hosts = list(self.csrf_trusted_origins_hosts)
|
||||
if good_referer is not None:
|
||||
good_hosts.append(good_referer)
|
||||
|
||||
|
|
|
@ -123,6 +123,9 @@ upgrading Django.
|
|||
* **2_0.W001**: Your URL pattern ``<pattern>`` has a ``route`` that contains
|
||||
``(?P<``, begins with a ``^``, or ends with a ``$``. This was likely an
|
||||
oversight when migrating from ``url()`` to :func:`~django.urls.path`.
|
||||
* **4_0.E001**: As of Django 4.0, the values in the
|
||||
:setting:`CSRF_TRUSTED_ORIGINS` setting must start with a scheme (usually
|
||||
``http://`` or ``https://``) but found ``<hostname>``.
|
||||
|
||||
Caches
|
||||
------
|
||||
|
|
|
@ -457,15 +457,24 @@ should be ``'HTTP_X_XSRF_TOKEN'``.
|
|||
|
||||
Default: ``[]`` (Empty list)
|
||||
|
||||
A list of hosts which are trusted origins for unsafe requests (e.g. ``POST``).
|
||||
A list of 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.
|
||||
The setting also supports subdomains, so you could add ``".example.com"``, for
|
||||
example, to allow access from all subdomains of ``example.com``.
|
||||
HTTPS, continuing the example, add ``'https://subdomain.example.com'`` to this
|
||||
list (and/or ``http://...`` if requests originate from an insecure page).
|
||||
|
||||
The setting also supports subdomains, so you could add
|
||||
``'https://*.example.com'``, for example, to allow access from all subdomains
|
||||
of ``example.com``.
|
||||
|
||||
.. versionchanged:: 4.0
|
||||
|
||||
The values in older versions must only include the hostname (possibly with
|
||||
a leading dot) and not the scheme or an asterisk.
|
||||
|
||||
.. setting:: DATABASES
|
||||
|
||||
|
|
|
@ -307,6 +307,22 @@ Upstream support for Oracle 12.2 ends in March 2022 and for Oracle 18c it ends
|
|||
in June 2021. Django 3.2 will be supported until April 2024. Django 4.0
|
||||
officially supports Oracle 19c.
|
||||
|
||||
.. _csrf-trusted-origins-changes-4.0:
|
||||
|
||||
``CSRF_TRUSTED_ORIGINS`` changes
|
||||
--------------------------------
|
||||
|
||||
Format change
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Values in the :setting:`CSRF_TRUSTED_ORIGINS` setting must include the scheme
|
||||
(e.g. ``'http://'`` or ``'https://'``) instead of only the hostname.
|
||||
|
||||
Also, values that started with a dot, must now also include an asterisk before
|
||||
the dot. For example, change ``'.example.com'`` to ``'https://*.example.com'``.
|
||||
|
||||
A system check detects any required changes.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
from django.core.checks import Error
|
||||
from django.core.checks.compatibility.django_4_0 import (
|
||||
check_csrf_trusted_origins,
|
||||
)
|
||||
from django.test import SimpleTestCase
|
||||
from django.test.utils import override_settings
|
||||
|
||||
|
||||
class CheckCSRFTrustedOrigins(SimpleTestCase):
|
||||
|
||||
@override_settings(CSRF_TRUSTED_ORIGINS=['example.com'])
|
||||
def test_invalid_url(self):
|
||||
self.assertEqual(check_csrf_trusted_origins(None), [
|
||||
Error(
|
||||
'As of Django 4.0, the values in the CSRF_TRUSTED_ORIGINS '
|
||||
'setting must start with a scheme (usually http:// or '
|
||||
'https://) but found example.com. See the release notes for '
|
||||
'details.',
|
||||
id='4_0.E001',
|
||||
)
|
||||
])
|
||||
|
||||
@override_settings(
|
||||
CSRF_TRUSTED_ORIGINS=['http://example.com', 'https://example.com'],
|
||||
)
|
||||
def test_valid_urls(self):
|
||||
self.assertEqual(check_csrf_trusted_origins(None), [])
|
|
@ -399,7 +399,7 @@ class CsrfViewMiddlewareTestMixin:
|
|||
resp = mw.process_view(req, post_form_view, (), {})
|
||||
self.assertIsNone(resp)
|
||||
|
||||
@override_settings(ALLOWED_HOSTS=['www.example.com'], CSRF_TRUSTED_ORIGINS=['dashboard.example.com'])
|
||||
@override_settings(ALLOWED_HOSTS=['www.example.com'], CSRF_TRUSTED_ORIGINS=['https://dashboard.example.com'])
|
||||
def test_https_csrf_trusted_origin_allowed(self):
|
||||
"""
|
||||
A POST HTTPS request with a referer added to the CSRF_TRUSTED_ORIGINS
|
||||
|
@ -414,7 +414,7 @@ class CsrfViewMiddlewareTestMixin:
|
|||
resp = mw.process_view(req, post_form_view, (), {})
|
||||
self.assertIsNone(resp)
|
||||
|
||||
@override_settings(ALLOWED_HOSTS=['www.example.com'], CSRF_TRUSTED_ORIGINS=['.example.com'])
|
||||
@override_settings(ALLOWED_HOSTS=['www.example.com'], CSRF_TRUSTED_ORIGINS=['https://*.example.com'])
|
||||
def test_https_csrf_wildcard_trusted_origin_allowed(self):
|
||||
"""
|
||||
A POST HTTPS request with a referer that matches a CSRF_TRUSTED_ORIGINS
|
||||
|
|
Loading…
Reference in New Issue