Skip to content

Commit 524b8de

Browse files
bpo-39603: Prevent header injection in http methods (GH-18485) (#21946)
reject control chars in http method in http.client.putrequest to prevent http header injection (cherry picked from commit 8ca8a2e) Co-authored-by: AMIR <31338382+amiremohamadi@users.noreply.github.com>
1 parent 6c06559 commit 524b8de

File tree

3 files changed

+42
-2
lines changed

3 files changed

+42
-2
lines changed

Lib/http/client.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@
151151
# _is_allowed_url_pchars_re = re.compile(r"^[/!$&'()*+,;=:@%a-zA-Z0-9._~-]+$")
152152
# We are more lenient for assumed real world compatibility purposes.
153153

154+
# These characters are not allowed within HTTP method names
155+
# to prevent http header injection.
156+
_contains_disallowed_method_pchar_re = re.compile('[\x00-\x1f]')
157+
154158
# We always set the Content-Length header for these methods because some
155159
# servers will otherwise respond with a 411
156160
_METHODS_EXPECTING_BODY = {'PATCH', 'POST', 'PUT'}
@@ -985,6 +989,8 @@ def putrequest(self, method, url, skip_host=False,
985989
else:
986990
raise CannotSendRequest(self.__state)
987991

992+
self._validate_method(method)
993+
988994
# Save the method for use later in the response phase
989995
self._method = method
990996

@@ -1075,6 +1081,16 @@ def _encode_request(self, request):
10751081
# ASCII also helps prevent CVE-2019-9740.
10761082
return request.encode('ascii')
10771083

1084+
def _validate_method(self, method):
1085+
"""Validate a method name for putrequest."""
1086+
# prevent http header injection
1087+
match = _contains_disallowed_method_pchar_re.search(method)
1088+
if match:
1089+
raise ValueError(
1090+
"method can't contain control characters. %r "
1091+
"(found at least %r)"
1092+
% (method, match.group()))
1093+
10781094
def _validate_path(self, url):
10791095
"""Validate a url for putrequest."""
10801096
# Prevent CVE-2019-9740.

Lib/test/test_httplib.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,28 @@ def test_invalid_headers(self):
344344
conn.putheader(name, value)
345345

346346

347+
class HttpMethodTests(TestCase):
348+
def test_invalid_method_names(self):
349+
methods = (
350+
'GET\r',
351+
'POST\n',
352+
'PUT\n\r',
353+
'POST\nValue',
354+
'POST\nHOST:abc',
355+
'GET\nrHost:abc\n',
356+
'POST\rRemainder:\r',
357+
'GET\rHOST:\n',
358+
'\nPUT'
359+
)
360+
361+
for method in methods:
362+
with self.assertRaisesRegex(
363+
ValueError, "method can't contain control characters"):
364+
conn = client.HTTPConnection('example.com')
365+
conn.sock = FakeSocket(None)
366+
conn.request(method=method, url="/")
367+
368+
347369
class BasicTest(TestCase):
348370
def test_status_lines(self):
349371
# Test HTTP status lines
@@ -1783,8 +1805,8 @@ def test_tunnel_debuglog(self):
17831805

17841806
@support.reap_threads
17851807
def test_main(verbose=None):
1786-
support.run_unittest(HeaderTests, OfflineTest, BasicTest, TimeoutTest,
1787-
PersistenceTest,
1808+
support.run_unittest(HeaderTests, OfflineTest, HttpMethodTests,
1809+
BasicTest, TimeoutTest, PersistenceTest,
17881810
HTTPSTest, RequestBodyTest, SourceAddressTest,
17891811
HTTPResponseTest, ExtendedReadTest,
17901812
ExtendedReadTestChunked, TunnelTests)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Prevent http header injection by rejecting control characters in
2+
http.client.putrequest(...).

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy