[CVE-2020-26116]Prevent header injection in http methods 36/257436/1
authorJinWang An <jinwang.an@samsung.com>
Mon, 26 Apr 2021 04:34:30 +0000 (13:34 +0900)
committerJinWang An <jinwang.an@samsung.com>
Mon, 26 Apr 2021 04:36:08 +0000 (13:36 +0900)
reject control chars in http method in http.client.putrequest
to prevent http header injection

Change-Id: I0f9d8bef27edd867a631aed06147f4d1f01b0c7b
Signed-off-by: JinWang An <jinwang.an@samsung.com>
Lib/http/client.py
Lib/test/test_httplib.py

index 5aa178d7b127d90de276dbd627c48304f39eacbf..09ffa7510f71f086f6a61269591e87edbc8b67e5 100644 (file)
@@ -140,6 +140,10 @@ _MAXHEADERS = 100
 _is_legal_header_name = re.compile(rb'[^:\s][^:\r\n]*').fullmatch
 _is_illegal_header_value = re.compile(rb'\n(?![ \t])|\r(?![ \t\n])').search
 
+# These characters are not allowed within HTTP method names
+# to prevent http header injection.
+_contains_disallowed_method_pchar_re = re.compile('[\x00-\x1f]')
+
 # We always set the Content-Length header for these methods because some
 # servers will otherwise respond with a 411
 _METHODS_EXPECTING_BODY = {'PATCH', 'POST', 'PUT'}
@@ -1097,6 +1101,9 @@ class HTTPConnection:
         else:
             raise CannotSendRequest(self.__state)
 
+        self._validate_method(method)
+
+
         # Save the method we use, we need it later in the response phase
         self._method = method
         if not url:
@@ -1274,6 +1281,15 @@ class HTTPConnection:
             body = _encode(body, 'body')
         self.endheaders(body, encode_chunked=encode_chunked)
 
+    def _validate_method(self, method):
+        """Validate a method name for putrequest."""
+        # prevent http header injection
+        match = _contains_disallowed_method_pchar_re.search(method)
+        if match:
+            raise ValueError(
+                    f"method can't contain control characters. {method!r} "
+                    f"(found at least {match.group()!r})")
+
     def getresponse(self):
         """Get the response from the server.
 
index f816eac83b682dfa725c389db967446c597c0b0f..c731ddb71f26d5d15bbe0bb6bf4b8a04ae38adc4 100644 (file)
@@ -360,6 +360,28 @@ class HeaderTests(TestCase):
         self.assertEqual(lines[2], "header: Second: val")
 
 
+class HttpMethodTests(TestCase):
+    def test_invalid_method_names(self):
+        methods = (
+            'GET\r',
+            'POST\n',
+            'PUT\n\r',
+            'POST\nValue',
+            'POST\nHOST:abc',
+            'GET\nrHost:abc\n',
+            'POST\rRemainder:\r',
+            'GET\rHOST:\n',
+            '\nPUT'
+        )
+
+        for method in methods:
+            with self.assertRaisesRegex(
+                    ValueError, "method can't contain control characters"):
+                conn = client.HTTPConnection('example.com')
+                conn.sock = FakeSocket(None)
+                conn.request(method=method, url="/")
+
+
 class TransferEncodingTest(TestCase):
     expected_body = b"It's just a flesh wound"