Fixed CVE-2019-12781 -- Made HttpRequest always trust SECURE_PROXY_SSL_HEADER if set.
An HTTP request would not be redirected to HTTPS when the SECURE_PROXY_SSL_HEADER and SECURE_SSL_REDIRECT settings were used if the proxy connected to Django via HTTPS. HttpRequest.scheme will now always trust the SECURE_PROXY_SSL_HEADER if set, rather than falling back to the request scheme when the SECURE_PROXY_SSL_HEADER did not have the secure value. Thanks to Gavin Wahl for the report and initial patch suggestion, and Shai Berger for review.
This commit is contained in:
parent
30b3ee9d0b
commit
54d0f5e62f
|
@ -226,13 +226,14 @@ class HttpRequest:
|
|||
def scheme(self):
|
||||
if settings.SECURE_PROXY_SSL_HEADER:
|
||||
try:
|
||||
header, value = settings.SECURE_PROXY_SSL_HEADER
|
||||
header, secure_value = settings.SECURE_PROXY_SSL_HEADER
|
||||
except ValueError:
|
||||
raise ImproperlyConfigured(
|
||||
'The SECURE_PROXY_SSL_HEADER setting must be a tuple containing two values.'
|
||||
)
|
||||
if self.META.get(header) == value:
|
||||
return 'https'
|
||||
header_value = self.META.get(header)
|
||||
if header_value is not None:
|
||||
return 'https' if header_value == secure_value else 'http'
|
||||
return self._get_scheme()
|
||||
|
||||
def is_secure(self):
|
||||
|
|
|
@ -2253,10 +2253,13 @@ By default, ``is_secure()`` determines if a request is secure by confirming
|
|||
that a requested URL uses ``https://``. This method is important for Django's
|
||||
CSRF protection, and it may be used by your own code or third-party apps.
|
||||
|
||||
If your Django app is behind a proxy, though, the proxy may be "swallowing" the
|
||||
fact that a request is HTTPS, using a non-HTTPS connection between the proxy
|
||||
and Django. In this case, ``is_secure()`` would always return ``False`` -- even
|
||||
for requests that were made via HTTPS by the end user.
|
||||
If your Django app is behind a proxy, though, the proxy may be "swallowing"
|
||||
whether the original request uses HTTPS or not. If there is a non-HTTPS
|
||||
connection between the proxy and Django then ``is_secure()`` would always
|
||||
return ``False`` -- even for requests that were made via HTTPS by the end user.
|
||||
In contrast, if there is an HTTPS connection between the proxy and Django then
|
||||
``is_secure()`` would always return ``True`` -- even for requests that were
|
||||
made originally via HTTP.
|
||||
|
||||
In this situation, configure your proxy to set a custom HTTP header that tells
|
||||
Django whether the request came in via HTTPS, and set
|
||||
|
|
|
@ -5,3 +5,23 @@ Django 1.11.22 release notes
|
|||
*July 1, 2019*
|
||||
|
||||
Django 1.11.22 fixes a security issue in 1.11.21.
|
||||
|
||||
CVE-2019-12781: Incorrect HTTP detection with reverse-proxy connecting via HTTPS
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
When deployed behind a reverse-proxy connecting to Django via HTTPS,
|
||||
:attr:`django.http.HttpRequest.scheme` would incorrectly detect client
|
||||
requests made via HTTP as using HTTPS. This entails incorrect results for
|
||||
:meth:`~django.http.HttpRequest.is_secure`, and
|
||||
:meth:`~django.http.HttpRequest.build_absolute_uri`, and that HTTP
|
||||
requests would not be redirected to HTTPS in accordance with
|
||||
:setting:`SECURE_SSL_REDIRECT`.
|
||||
|
||||
``HttpRequest.scheme`` now respects :setting:`SECURE_PROXY_SSL_HEADER`, if it
|
||||
is configured, and the appropriate header is set on the request, for both HTTP
|
||||
and HTTPS requests.
|
||||
|
||||
If you deploy Django behind a reverse-proxy that forwards HTTP requests, and
|
||||
that connects to Django via HTTPS, be sure to verify that your application
|
||||
correctly handles code paths relying on ``scheme``, ``is_secure()``,
|
||||
``build_absolute_uri()``, and ``SECURE_SSL_REDIRECT``.
|
||||
|
|
|
@ -5,3 +5,23 @@ Django 2.1.10 release notes
|
|||
*July 1, 2019*
|
||||
|
||||
Django 2.1.10 fixes a security issue in 2.1.9.
|
||||
|
||||
CVE-2019-12781: Incorrect HTTP detection with reverse-proxy connecting via HTTPS
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
When deployed behind a reverse-proxy connecting to Django via HTTPS,
|
||||
:attr:`django.http.HttpRequest.scheme` would incorrectly detect client
|
||||
requests made via HTTP as using HTTPS. This entails incorrect results for
|
||||
:meth:`~django.http.HttpRequest.is_secure`, and
|
||||
:meth:`~django.http.HttpRequest.build_absolute_uri`, and that HTTP
|
||||
requests would not be redirected to HTTPS in accordance with
|
||||
:setting:`SECURE_SSL_REDIRECT`.
|
||||
|
||||
``HttpRequest.scheme`` now respects :setting:`SECURE_PROXY_SSL_HEADER`, if it
|
||||
is configured, and the appropriate header is set on the request, for both HTTP
|
||||
and HTTPS requests.
|
||||
|
||||
If you deploy Django behind a reverse-proxy that forwards HTTP requests, and
|
||||
that connects to Django via HTTPS, be sure to verify that your application
|
||||
correctly handles code paths relying on ``scheme``, ``is_secure()``,
|
||||
``build_absolute_uri()``, and ``SECURE_SSL_REDIRECT``.
|
||||
|
|
|
@ -4,8 +4,28 @@ Django 2.2.3 release notes
|
|||
|
||||
*Expected July 1, 2019*
|
||||
|
||||
Django 2.2.3 fixes several bugs in 2.2.2. Also, the latest string translations
|
||||
from Transifex are incorporated.
|
||||
Django 2.2.3 fixes a security issue and several bugs in 2.2.2. Also, the latest
|
||||
string translations from Transifex are incorporated.
|
||||
|
||||
CVE-2019-12781: Incorrect HTTP detection with reverse-proxy connecting via HTTPS
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
When deployed behind a reverse-proxy connecting to Django via HTTPS,
|
||||
:attr:`django.http.HttpRequest.scheme` would incorrectly detect client
|
||||
requests made via HTTP as using HTTPS. This entails incorrect results for
|
||||
:meth:`~django.http.HttpRequest.is_secure`, and
|
||||
:meth:`~django.http.HttpRequest.build_absolute_uri`, and that HTTP
|
||||
requests would not be redirected to HTTPS in accordance with
|
||||
:setting:`SECURE_SSL_REDIRECT`.
|
||||
|
||||
``HttpRequest.scheme`` now respects :setting:`SECURE_PROXY_SSL_HEADER`, if it is
|
||||
configured, and the appropriate header is set on the request, for both HTTP and
|
||||
HTTPS requests.
|
||||
|
||||
If you deploy Django behind a reverse-proxy that forwards HTTP requests, and
|
||||
that connects to Django via HTTPS, be sure to verify that your application
|
||||
correctly handles code paths relying on ``scheme``, ``is_secure()``,
|
||||
``build_absolute_uri()``, and ``SECURE_SSL_REDIRECT``.
|
||||
|
||||
Bugfixes
|
||||
========
|
||||
|
|
|
@ -378,6 +378,18 @@ class SecureProxySslHeaderTest(SimpleTestCase):
|
|||
req.META['HTTP_X_FORWARDED_PROTOCOL'] = 'https'
|
||||
self.assertIs(req.is_secure(), True)
|
||||
|
||||
@override_settings(SECURE_PROXY_SSL_HEADER=('HTTP_X_FORWARDED_PROTOCOL', 'https'))
|
||||
def test_xheader_preferred_to_underlying_request(self):
|
||||
class ProxyRequest(HttpRequest):
|
||||
def _get_scheme(self):
|
||||
"""Proxy always connecting via HTTPS"""
|
||||
return 'https'
|
||||
|
||||
# Client connects via HTTP.
|
||||
req = ProxyRequest()
|
||||
req.META['HTTP_X_FORWARDED_PROTOCOL'] = 'http'
|
||||
self.assertIs(req.is_secure(), False)
|
||||
|
||||
|
||||
class IsOverriddenTest(SimpleTestCase):
|
||||
def test_configure(self):
|
||||
|
|
Loading…
Reference in New Issue