Browse Source

Fixed #33569 -- Added SECURE_PROXY_SSL_HEADER support for list of protocols in the header value.

Thomas Schmidt 3 năm trước cách đây
mục cha
commit
1cf60ce601
4 tập tin đã thay đổi với 39 bổ sung5 xóa
  1. 2 1
      django/http/request.py
  2. 14 4
      docs/ref/settings.txt
  3. 3 0
      docs/releases/4.1.txt
  4. 20 0
      tests/settings_tests/tests.py

+ 2 - 1
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):

+ 14 - 4
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.
 

+ 3 - 0
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
 ~~~~~~~~~~~~~
 

+ 20 - 0
tests/settings_tests/tests.py

@@ -424,6 +424,26 @@ class SecureProxySslHeaderTest(SimpleTestCase):
         req.META["HTTP_X_FORWARDED_PROTO"] = "https"
         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):