From 3022ce1a14b364a1d1d69405646aae892d827666 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Thu, 16 Dec 2010 17:03:16 +0000 Subject: [PATCH] Merged revisions 87317 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ........ r87317 | antoine.pitrou | 2010-12-16 17:48:36 +0100 (jeu., 16 déc. 2010) | 4 lines Issue #10714: Limit length of incoming request in http.server to 65536 bytes for security reasons. Initial patch by Ross Lagerwall. ........ (also backport some tests) --- Lib/http/server.py | 8 ++- Lib/test/test_httpservers.py | 101 +++++++++++++++++++++++++++++++++++ Misc/ACKS | 1 + Misc/NEWS | 3 ++ 4 files changed, 112 insertions(+), 1 deletion(-) diff --git a/Lib/http/server.py b/Lib/http/server.py index c5b00d69229..5ac6c0d204e 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -333,7 +333,13 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): commands such as GET and POST. """ - self.raw_requestline = self.rfile.readline() + self.raw_requestline = self.rfile.readline(65537) + if len(self.raw_requestline) > 65536: + self.requestline = '' + self.request_version = '' + self.command = '' + self.send_error(414) + return if not self.raw_requestline: self.close_connection = 1 return diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 6c849a11286..cbc77f20cfa 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -478,6 +478,107 @@ class CGIHTTPServerTestCase(BaseTestCase): (res.read(), res.getheader('Content-type'), res.status)) self.assertEqual(os.environ['SERVER_SOFTWARE'], signature) + +class SocketlessRequestHandler(SimpleHTTPRequestHandler): + def __init__(self): + self.get_called = False + self.protocol_version = "HTTP/1.1" + + def do_GET(self): + self.get_called = True + self.send_response(200) + self.send_header('Content-Type', 'text/html') + self.end_headers() + self.wfile.write(b'Data\r\n') + + def log_message(self, format, *args): + pass + +class BaseHTTPRequestHandlerTestCase(unittest.TestCase): + """Test the functionaility of the BaseHTTPServer. + """ + + HTTPResponseMatch = re.compile(b'HTTP/1.[0-9]+ 200 OK') + + def setUp (self): + self.handler = SocketlessRequestHandler() + + def send_typical_request(self, message): + input = BytesIO(message) + output = BytesIO() + self.handler.rfile = input + self.handler.wfile = output + self.handler.handle_one_request() + output.seek(0) + return output.readlines() + + def verify_get_called(self): + self.assertTrue(self.handler.get_called) + + def verify_expected_headers(self, headers): + for fieldName in b'Server: ', b'Date: ', b'Content-Type: ': + self.assertEqual(sum(h.startswith(fieldName) for h in headers), 1) + + def verify_http_server_response(self, response): + match = self.HTTPResponseMatch.search(response) + self.assertTrue(match is not None) + + def test_http_1_1(self): + result = self.send_typical_request(b'GET / HTTP/1.1\r\n\r\n') + self.verify_http_server_response(result[0]) + self.verify_expected_headers(result[1:-1]) + self.verify_get_called() + self.assertEqual(result[-1], b'Data\r\n') + + def test_http_1_0(self): + result = self.send_typical_request(b'GET / HTTP/1.0\r\n\r\n') + self.verify_http_server_response(result[0]) + self.verify_expected_headers(result[1:-1]) + self.verify_get_called() + self.assertEqual(result[-1], b'Data\r\n') + + def test_http_0_9(self): + result = self.send_typical_request(b'GET / HTTP/0.9\r\n\r\n') + self.assertEqual(len(result), 1) + self.assertEqual(result[0], b'Data\r\n') + self.verify_get_called() + + def test_with_continue_1_0(self): + result = self.send_typical_request(b'GET / HTTP/1.0\r\nExpect: 100-continue\r\n\r\n') + self.verify_http_server_response(result[0]) + self.verify_expected_headers(result[1:-1]) + self.verify_get_called() + self.assertEqual(result[-1], b'Data\r\n') + + def test_request_length(self): + # Issue #10714: huge request lines are discarded, to avoid Denial + # of Service attacks. + result = self.send_typical_request(b'GET ' + b'x' * 65537) + self.assertEqual(result[0], b'HTTP/1.1 414 Request-URI Too Long\r\n') + self.assertFalse(self.handler.get_called) + +class SimpleHTTPRequestHandlerTestCase(unittest.TestCase): + """ Test url parsing """ + def setUp(self): + self.translated = os.getcwd() + self.translated = os.path.join(self.translated, 'filename') + self.handler = SocketlessRequestHandler() + + def test_query_arguments(self): + path = self.handler.translate_path('/filename') + self.assertEqual(path, self.translated) + path = self.handler.translate_path('/filename?foo=bar') + self.assertEqual(path, self.translated) + path = self.handler.translate_path('/filename?a=b&spam=eggs#zot') + self.assertEqual(path, self.translated) + + def test_start_with_double_slash(self): + path = self.handler.translate_path('//filename') + self.assertEqual(path, self.translated) + path = self.handler.translate_path('//filename?foo=bar') + self.assertEqual(path, self.translated) + + def test_main(verbose=None): try: cwd = os.getcwd() diff --git a/Misc/ACKS b/Misc/ACKS index 83527151c14..03f73a29d27 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -442,6 +442,7 @@ Andrej Krpic Ivan Krstić Andrew Kuchling Vladimir Kushnir +Ross Lagerwall Cameron Laird Łukasz Langa Tino Lange diff --git a/Misc/NEWS b/Misc/NEWS index d8fd483abba..2b09d5fc676 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,9 @@ Core and Builtins Library ------- +- Issue #10714: Limit length of incoming request in http.server to 65536 bytes + for security reasons. Initial patch by Ross Lagerwall. + - Issue #9558: Fix distutils.command.build_ext with VS 8.0. - Issue #10695: passing the port as a string value to telnetlib no longer