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 these to force registration of checks
|
||||||
import django.core.checks.async_checks # NOQA isort:skip
|
import django.core.checks.async_checks # NOQA isort:skip
|
||||||
import django.core.checks.caches # 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.database # NOQA isort:skip
|
||||||
import django.core.checks.files # NOQA isort:skip
|
import django.core.checks.files # NOQA isort:skip
|
||||||
import django.core.checks.model_checks # 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.cache import patch_vary_headers
|
||||||
from django.utils.crypto import constant_time_compare, get_random_string
|
from django.utils.crypto import constant_time_compare, get_random_string
|
||||||
from django.utils.deprecation import MiddlewareMixin
|
from django.utils.deprecation import MiddlewareMixin
|
||||||
|
from django.utils.functional import cached_property
|
||||||
from django.utils.http import is_same_domain
|
from django.utils.http import is_same_domain
|
||||||
from django.utils.log import log_response
|
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 %}
|
This middleware should be used in conjunction with the {% csrf_token %}
|
||||||
template tag.
|
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
|
# The _accept and _reject methods currently only exist for the sake of the
|
||||||
# requires_csrf_token decorator.
|
# requires_csrf_token decorator.
|
||||||
def _accept(self, request):
|
def _accept(self, request):
|
||||||
|
@ -272,7 +280,7 @@ class CsrfViewMiddleware(MiddlewareMixin):
|
||||||
|
|
||||||
# Create a list of all acceptable HTTP referers, including the
|
# Create a list of all acceptable HTTP referers, including the
|
||||||
# current host if it's permitted by ALLOWED_HOSTS.
|
# 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:
|
if good_referer is not None:
|
||||||
good_hosts.append(good_referer)
|
good_hosts.append(good_referer)
|
||||||
|
|
||||||
|
|
|
@ -123,6 +123,9 @@ upgrading Django.
|
||||||
* **2_0.W001**: Your URL pattern ``<pattern>`` has a ``route`` that contains
|
* **2_0.W001**: Your URL pattern ``<pattern>`` has a ``route`` that contains
|
||||||
``(?P<``, begins with a ``^``, or ends with a ``$``. This was likely an
|
``(?P<``, begins with a ``^``, or ends with a ``$``. This was likely an
|
||||||
oversight when migrating from ``url()`` to :func:`~django.urls.path`.
|
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
|
Caches
|
||||||
------
|
------
|
||||||
|
|
|
@ -457,15 +457,24 @@ should be ``'HTTP_X_XSRF_TOKEN'``.
|
||||||
|
|
||||||
Default: ``[]`` (Empty list)
|
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
|
For a :meth:`secure <django.http.HttpRequest.is_secure>` unsafe
|
||||||
request, Django's CSRF protection requires that the request have a ``Referer``
|
request, Django's CSRF protection requires that the request have a ``Referer``
|
||||||
header that matches the origin present in the ``Host`` header. This prevents,
|
header that matches the origin present in the ``Host`` header. This prevents,
|
||||||
for example, a ``POST`` request from ``subdomain.example.com`` from succeeding
|
for example, a ``POST`` request from ``subdomain.example.com`` from succeeding
|
||||||
against ``api.example.com``. If you need cross-origin unsafe requests over
|
against ``api.example.com``. If you need cross-origin unsafe requests over
|
||||||
HTTPS, continuing the example, add ``"subdomain.example.com"`` to this list.
|
HTTPS, continuing the example, add ``'https://subdomain.example.com'`` to this
|
||||||
The setting also supports subdomains, so you could add ``".example.com"``, for
|
list (and/or ``http://...`` if requests originate from an insecure page).
|
||||||
example, to allow access from all subdomains of ``example.com``.
|
|
||||||
|
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
|
.. 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
|
in June 2021. Django 3.2 will be supported until April 2024. Django 4.0
|
||||||
officially supports Oracle 19c.
|
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
|
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, (), {})
|
resp = mw.process_view(req, post_form_view, (), {})
|
||||||
self.assertIsNone(resp)
|
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):
|
def test_https_csrf_trusted_origin_allowed(self):
|
||||||
"""
|
"""
|
||||||
A POST HTTPS request with a referer added to the CSRF_TRUSTED_ORIGINS
|
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, (), {})
|
resp = mw.process_view(req, post_form_view, (), {})
|
||||||
self.assertIsNone(resp)
|
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):
|
def test_https_csrf_wildcard_trusted_origin_allowed(self):
|
||||||
"""
|
"""
|
||||||
A POST HTTPS request with a referer that matches a CSRF_TRUSTED_ORIGINS
|
A POST HTTPS request with a referer that matches a CSRF_TRUSTED_ORIGINS
|
||||||
|
|
Loading…
Reference in New Issue