Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1e40f42

Browse files
carltongibsonfelixxm
authored andcommitted
[2.1.x] 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. Backport of 54d0f5e from master
1 parent 87be9c9 commit 1e40f42

File tree

5 files changed

+63
-7
lines changed

5 files changed

+63
-7
lines changed

django/http/request.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,13 +210,14 @@ def _get_scheme(self):
210210
def scheme(self):
211211
if settings.SECURE_PROXY_SSL_HEADER:
212212
try:
213-
header, value = settings.SECURE_PROXY_SSL_HEADER
213+
header, secure_value = settings.SECURE_PROXY_SSL_HEADER
214214
except ValueError:
215215
raise ImproperlyConfigured(
216216
'The SECURE_PROXY_SSL_HEADER setting must be a tuple containing two values.'
217217
)
218-
if self.META.get(header) == value:
219-
return 'https'
218+
header_value = self.META.get(header)
219+
if header_value is not None:
220+
return 'https' if header_value == secure_value else 'http'
220221
return self._get_scheme()
221222

222223
def is_secure(self):

docs/ref/settings.txt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2198,10 +2198,13 @@ By default, ``is_secure()`` determines if a request is secure by confirming
21982198
that a requested URL uses ``https://``. This method is important for Django's
21992199
CSRF protection, and it may be used by your own code or third-party apps.
22002200

2201-
If your Django app is behind a proxy, though, the proxy may be "swallowing" the
2202-
fact that a request is HTTPS, using a non-HTTPS connection between the proxy
2203-
and Django. In this case, ``is_secure()`` would always return ``False`` -- even
2204-
for requests that were made via HTTPS by the end user.
2201+
If your Django app is behind a proxy, though, the proxy may be "swallowing"
2202+
whether the original request uses HTTPS or not. If there is a non-HTTPS
2203+
connection between the proxy and Django then ``is_secure()`` would always
2204+
return ``False`` -- even for requests that were made via HTTPS by the end user.
2205+
In contrast, if there is an HTTPS connection between the proxy and Django then
2206+
``is_secure()`` would always return ``True`` -- even for requests that were
2207+
made originally via HTTP.
22052208

22062209
In this situation, configure your proxy to set a custom HTTP header that tells
22072210
Django whether the request came in via HTTPS, and set

docs/releases/1.11.22.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,23 @@ Django 1.11.22 release notes
55
*July 1, 2019*
66

77
Django 1.11.22 fixes a security issue in 1.11.21.
8+
9+
CVE-2019-12781: Incorrect HTTP detection with reverse-proxy connecting via HTTPS
10+
--------------------------------------------------------------------------------
11+
12+
When deployed behind a reverse-proxy connecting to Django via HTTPS,
13+
:attr:`django.http.HttpRequest.scheme` would incorrectly detect client
14+
requests made via HTTP as using HTTPS. This entails incorrect results for
15+
:meth:`~django.http.HttpRequest.is_secure`, and
16+
:meth:`~django.http.HttpRequest.build_absolute_uri`, and that HTTP
17+
requests would not be redirected to HTTPS in accordance with
18+
:setting:`SECURE_SSL_REDIRECT`.
19+
20+
``HttpRequest.scheme`` now respects :setting:`SECURE_PROXY_SSL_HEADER`, if it
21+
is configured, and the appropriate header is set on the request, for both HTTP
22+
and HTTPS requests.
23+
24+
If you deploy Django behind a reverse-proxy that forwards HTTP requests, and
25+
that connects to Django via HTTPS, be sure to verify that your application
26+
correctly handles code paths relying on ``scheme``, ``is_secure()``,
27+
``build_absolute_uri()``, and ``SECURE_SSL_REDIRECT``.

docs/releases/2.1.10.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,23 @@ Django 2.1.10 release notes
55
*July 1, 2019*
66

77
Django 2.1.10 fixes a security issue in 2.1.9.
8+
9+
CVE-2019-12781: Incorrect HTTP detection with reverse-proxy connecting via HTTPS
10+
--------------------------------------------------------------------------------
11+
12+
When deployed behind a reverse-proxy connecting to Django via HTTPS,
13+
:attr:`django.http.HttpRequest.scheme` would incorrectly detect client
14+
requests made via HTTP as using HTTPS. This entails incorrect results for
15+
:meth:`~django.http.HttpRequest.is_secure`, and
16+
:meth:`~django.http.HttpRequest.build_absolute_uri`, and that HTTP
17+
requests would not be redirected to HTTPS in accordance with
18+
:setting:`SECURE_SSL_REDIRECT`.
19+
20+
``HttpRequest.scheme`` now respects :setting:`SECURE_PROXY_SSL_HEADER`, if it
21+
is configured, and the appropriate header is set on the request, for both HTTP
22+
and HTTPS requests.
23+
24+
If you deploy Django behind a reverse-proxy that forwards HTTP requests, and
25+
that connects to Django via HTTPS, be sure to verify that your application
26+
correctly handles code paths relying on ``scheme``, ``is_secure()``,
27+
``build_absolute_uri()``, and ``SECURE_SSL_REDIRECT``.

tests/settings_tests/tests.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,18 @@ def test_set_with_xheader_right(self):
367367
req.META['HTTP_X_FORWARDED_PROTOCOL'] = 'https'
368368
self.assertIs(req.is_secure(), True)
369369

370+
@override_settings(SECURE_PROXY_SSL_HEADER=('HTTP_X_FORWARDED_PROTOCOL', 'https'))
371+
def test_xheader_preferred_to_underlying_request(self):
372+
class ProxyRequest(HttpRequest):
373+
def _get_scheme(self):
374+
"""Proxy always connecting via HTTPS"""
375+
return 'https'
376+
377+
# Client connects via HTTP.
378+
req = ProxyRequest()
379+
req.META['HTTP_X_FORWARDED_PROTOCOL'] = 'http'
380+
self.assertIs(req.is_secure(), False)
381+
370382

371383
class IsOverriddenTest(SimpleTestCase):
372384
def test_configure(self):

0 commit comments

Comments
 (0)