File tree Expand file tree Collapse file tree 4 files changed +43
-7
lines changed Expand file tree Collapse file tree 4 files changed +43
-7
lines changed Original file line number Diff line number Diff line change @@ -199,13 +199,14 @@ def _get_scheme(self):
199
199
def scheme (self ):
200
200
if settings .SECURE_PROXY_SSL_HEADER :
201
201
try :
202
- header , value = settings .SECURE_PROXY_SSL_HEADER
202
+ header , secure_value = settings .SECURE_PROXY_SSL_HEADER
203
203
except ValueError :
204
204
raise ImproperlyConfigured (
205
205
'The SECURE_PROXY_SSL_HEADER setting must be a tuple containing two values.'
206
206
)
207
- if self .META .get (header ) == value :
208
- return 'https'
207
+ header_value = self .META .get (header )
208
+ if header_value is not None :
209
+ return 'https' if header_value == secure_value else 'http'
209
210
return self ._get_scheme ()
210
211
211
212
def is_secure (self ):
Original file line number Diff line number Diff line change @@ -2189,10 +2189,13 @@ whether a request is secure by looking at whether the requested URL uses
2189
2189
"https://". This is important for Django's CSRF protection, and may be used
2190
2190
by your own code or third-party apps.
2191
2191
2192
- If your Django app is behind a proxy, though, the proxy may be "swallowing" the
2193
- fact that a request is HTTPS, using a non-HTTPS connection between the proxy
2194
- and Django. In this case, ``is_secure()`` would always return ``False`` -- even
2195
- for requests that were made via HTTPS by the end user.
2192
+ If your Django app is behind a proxy, though, the proxy may be "swallowing"
2193
+ whether the original request uses HTTPS or not. If there is a non-HTTPS
2194
+ connection between the proxy and Django then ``is_secure()`` would always
2195
+ return ``False`` -- even for requests that were made via HTTPS by the end user.
2196
+ In contrast, if there is an HTTPS connection between the proxy and Django then
2197
+ ``is_secure()`` would always return ``True`` -- even for requests that were
2198
+ made originally via HTTP.
2196
2199
2197
2200
In this situation, you'll want to configure your proxy to set a custom HTTP
2198
2201
header that tells Django whether the request came in via HTTPS, and you'll want
Original file line number Diff line number Diff line change @@ -5,3 +5,23 @@ Django 1.11.22 release notes
5
5
*July 1, 2019*
6
6
7
7
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``.
Original file line number Diff line number Diff line change @@ -334,6 +334,18 @@ def test_set_with_xheader_right(self):
334
334
req .META ['HTTP_X_FORWARDED_PROTOCOL' ] = 'https'
335
335
self .assertIs (req .is_secure (), True )
336
336
337
+ @override_settings (SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL' , 'https' ))
338
+ def test_xheader_preferred_to_underlying_request (self ):
339
+ class ProxyRequest (HttpRequest ):
340
+ def _get_scheme (self ):
341
+ """Proxy always connecting via HTTPS"""
342
+ return 'https'
343
+
344
+ # Client connects via HTTP.
345
+ req = ProxyRequest ()
346
+ req .META ['HTTP_X_FORWARDED_PROTOCOL' ] = 'http'
347
+ self .assertIs (req .is_secure (), False )
348
+
337
349
338
350
class IsOverriddenTest (SimpleTestCase ):
339
351
def test_configure (self ):
You can’t perform that action at this time.
0 commit comments