Skip to content

Commit

Permalink
[2.2.x] Fixed CVE-2019-12781 -- Made HttpRequest always trust SECURE_…
Browse files Browse the repository at this point in the history
…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.

Backport of 54d0f5e from master
  • Loading branch information
carltongibson authored and felixxm committed Jul 1, 2019
1 parent db9f7b4 commit 77706a3
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 9 deletions.
7 changes: 4 additions & 3 deletions django/http/request.py
Expand Up @@ -215,13 +215,14 @@ def _get_scheme(self):
def scheme(self): def scheme(self):
if settings.SECURE_PROXY_SSL_HEADER: if settings.SECURE_PROXY_SSL_HEADER:
try: try:
header, value = settings.SECURE_PROXY_SSL_HEADER header, secure_value = settings.SECURE_PROXY_SSL_HEADER
except ValueError: except ValueError:
raise ImproperlyConfigured( raise ImproperlyConfigured(
'The SECURE_PROXY_SSL_HEADER setting must be a tuple containing two values.' 'The SECURE_PROXY_SSL_HEADER setting must be a tuple containing two values.'
) )
if self.META.get(header) == value: header_value = self.META.get(header)
return 'https' if header_value is not None:
return 'https' if header_value == secure_value else 'http'
return self._get_scheme() return self._get_scheme()


def is_secure(self): def is_secure(self):
Expand Down
11 changes: 7 additions & 4 deletions docs/ref/settings.txt
Expand Up @@ -2224,10 +2224,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 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. 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 If your Django app is behind a proxy, though, the proxy may be "swallowing"
fact that a request is HTTPS, using a non-HTTPS connection between the proxy whether the original request uses HTTPS or not. If there is a non-HTTPS
and Django. In this case, ``is_secure()`` would always return ``False`` -- even connection between the proxy and Django then ``is_secure()`` would always
for requests that were made via HTTPS by the end user. 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 In this situation, configure your proxy to set a custom HTTP header that tells
Django whether the request came in via HTTPS, and set Django whether the request came in via HTTPS, and set
Expand Down
20 changes: 20 additions & 0 deletions docs/releases/1.11.22.txt
Expand Up @@ -5,3 +5,23 @@ Django 1.11.22 release notes
*July 1, 2019* *July 1, 2019*


Django 1.11.22 fixes a security issue in 1.11.21. 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``.
20 changes: 20 additions & 0 deletions docs/releases/2.1.10.txt
Expand Up @@ -5,3 +5,23 @@ Django 2.1.10 release notes
*July 1, 2019* *July 1, 2019*


Django 2.1.10 fixes a security issue in 2.1.9. 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``.
24 changes: 22 additions & 2 deletions docs/releases/2.2.3.txt
Expand Up @@ -4,8 +4,28 @@ Django 2.2.3 release notes


*Expected July 1, 2019* *Expected July 1, 2019*


Django 2.2.3 fixes several bugs in 2.2.2. Also, the latest string translations Django 2.2.3 fixes a security issue and several bugs in 2.2.2. Also, the latest
from Transifex are incorporated. 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 Bugfixes
======== ========
Expand Down
12 changes: 12 additions & 0 deletions tests/settings_tests/tests.py
Expand Up @@ -367,6 +367,18 @@ def test_set_with_xheader_right(self):
req.META['HTTP_X_FORWARDED_PROTOCOL'] = 'https' req.META['HTTP_X_FORWARDED_PROTOCOL'] = 'https'
self.assertIs(req.is_secure(), True) 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): class IsOverriddenTest(SimpleTestCase):
def test_configure(self): def test_configure(self):
Expand Down

0 comments on commit 77706a3

Please sign in to comment.