From: JinWang An Date: Mon, 26 Apr 2021 04:34:30 +0000 (+0900) Subject: [CVE-2020-26116]Prevent header injection in http methods X-Git-Tag: submit/tizen_6.0_base/20210521.062029~9 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=56cec68533c40ef670e34101d6ca354b0f16b2e6;p=platform%2Fupstream%2Fpython3.git [CVE-2020-26116]Prevent header injection in http methods reject control chars in http method in http.client.putrequest to prevent http header injection Change-Id: I0f9d8bef27edd867a631aed06147f4d1f01b0c7b Signed-off-by: JinWang An --- diff --git a/Lib/http/client.py b/Lib/http/client.py index 5aa178d7..09ffa751 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -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. diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index f816eac8..c731ddb7 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -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"