summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Schmidt <thomas@useobject.com>2022-03-07 17:25:52 -0500
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2022-03-23 19:33:36 +0100
commit1cf60ce6017d904024ee132f7edae0b4b821a954 (patch)
treebb73a5fcca79e812f2c7182ddf68dadee7c49ea2
parentd46e158ee21cb067a65d44699e6dce00b3dab8e3 (diff)
downloaddjango-1cf60ce6017d904024ee132f7edae0b4b821a954.tar.gz
Fixed #33569 -- Added SECURE_PROXY_SSL_HEADER support for list of protocols in the header value.
-rw-r--r--django/http/request.py3
-rw-r--r--docs/ref/settings.txt18
-rw-r--r--docs/releases/4.1.txt3
-rw-r--r--tests/settings_tests/tests.py20
4 files changed, 39 insertions, 5 deletions
diff --git a/django/http/request.py b/django/http/request.py
index 0b64b1b2b9..c25fa4379a 100644
--- a/django/http/request.py
+++ b/django/http/request.py
@@ -261,7 +261,8 @@ class HttpRequest:
)
header_value = self.META.get(header)
if header_value is not None:
- return "https" if header_value == secure_value else "http"
+ header_value, *_ = header_value.split(",", 1)
+ return "https" if header_value.strip() == secure_value else "http"
return self._get_scheme()
def is_secure(self):
diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt
index 73a4b8b649..ae1b15eab0 100644
--- a/docs/ref/settings.txt
+++ b/docs/ref/settings.txt
@@ -2442,8 +2442,17 @@ required value. For example::
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
This tells Django to trust the ``X-Forwarded-Proto`` header that comes from our
-proxy, and any time its value is ``'https'``, then the request is guaranteed to
-be secure (i.e., it originally came in via HTTPS).
+proxy and that the request is guaranteed to be secure (i.e., it originally came
+in via HTTPS) when:
+
+* the header value is ``'https'``, or
+* its initial, leftmost value is ``'https'`` in the case of a comma-separated
+ list of protocols (e.g. ``'https,http,http'``).
+
+.. versionchanged:: 4.1
+
+ Support for a comma-separated list of protocols in the header value was
+ added.
You should *only* set this setting if you control your proxy or have some other
guarantee that it sets/strips this header appropriately.
@@ -2463,8 +2472,9 @@ available in ``request.META``.)
* Your Django app is behind a proxy.
* Your proxy strips the ``X-Forwarded-Proto`` header from all incoming
- requests. In other words, if end users include that header in their
- requests, the proxy will discard it.
+ requests, even when it contains a comma-separated list of protocols. In
+ other words, if end users include that header in their requests, the
+ proxy will discard it.
* Your proxy sets the ``X-Forwarded-Proto`` header and sends it to Django,
but only for requests that originally come in via HTTPS.
diff --git a/docs/releases/4.1.txt b/docs/releases/4.1.txt
index a61d5e8e11..b8dbc36da0 100644
--- a/docs/releases/4.1.txt
+++ b/docs/releases/4.1.txt
@@ -293,6 +293,9 @@ Security
* The new :setting:`SECRET_KEY_FALLBACKS` setting allows providing a list of
values for secret key rotation.
+* The :setting:`SECURE_PROXY_SSL_HEADER` setting now supports a comma-separated
+ list of protocols in the header value.
+
Serialization
~~~~~~~~~~~~~
diff --git a/tests/settings_tests/tests.py b/tests/settings_tests/tests.py
index 1b095533b1..f9ea7700bb 100644
--- a/tests/settings_tests/tests.py
+++ b/tests/settings_tests/tests.py
@@ -425,6 +425,26 @@ class SecureProxySslHeaderTest(SimpleTestCase):
self.assertIs(req.is_secure(), True)
@override_settings(SECURE_PROXY_SSL_HEADER=("HTTP_X_FORWARDED_PROTO", "https"))
+ def test_set_with_xheader_leftmost_right(self):
+ req = HttpRequest()
+ req.META["HTTP_X_FORWARDED_PROTO"] = "https, http"
+ self.assertIs(req.is_secure(), True)
+ req.META["HTTP_X_FORWARDED_PROTO"] = "https , http"
+ self.assertIs(req.is_secure(), True)
+
+ @override_settings(SECURE_PROXY_SSL_HEADER=("HTTP_X_FORWARDED_PROTO", "https"))
+ def test_set_with_xheader_leftmost_not_secure(self):
+ req = HttpRequest()
+ req.META["HTTP_X_FORWARDED_PROTO"] = "http, https"
+ self.assertIs(req.is_secure(), False)
+
+ @override_settings(SECURE_PROXY_SSL_HEADER=("HTTP_X_FORWARDED_PROTO", "https"))
+ def test_set_with_xheader_multiple_not_secure(self):
+ req = HttpRequest()
+ req.META["HTTP_X_FORWARDED_PROTO"] = "http ,wrongvalue,http,http"
+ self.assertIs(req.is_secure(), False)
+
+ @override_settings(SECURE_PROXY_SSL_HEADER=("HTTP_X_FORWARDED_PROTO", "https"))
def test_xheader_preferred_to_underlying_request(self):
class ProxyRequest(HttpRequest):
def _get_scheme(self):