diff --git a/lib/webrick/httprequest.rb b/lib/webrick/httprequest.rb index 7a1686b..43e4e8a 100644 --- a/lib/webrick/httprequest.rb +++ b/lib/webrick/httprequest.rb @@ -475,6 +475,9 @@ def read_header(socket) if (@request_bytes += line.bytesize) > MAX_HEADER_LENGTH raise HTTPStatus::RequestEntityTooLarge, 'headers too large' end + if line.include?("\x00") + raise HTTPStatus::BadRequest, 'null byte in header' + end @raw_header << line end end @@ -542,7 +545,7 @@ def read_body(socket, block) def read_chunk_size(socket) line = read_line(socket) - if /^([0-9a-fA-F]+)(?:;(\S+))?/ =~ line + if /\A([0-9a-fA-F]+)(?:;(\S+(?:=\S+)?))?\r\n\z/ =~ line chunk_size = $1.hex chunk_ext = $2 [ chunk_size, chunk_ext ] diff --git a/test/webrick/test_httprequest.rb b/test/webrick/test_httprequest.rb index 9033217..c0fb2e9 100644 --- a/test/webrick/test_httprequest.rb +++ b/test/webrick/test_httprequest.rb @@ -289,6 +289,40 @@ def test_chunked assert_equal(expect, dst.string) end + def test_bad_chunked + msg = <<-_end_of_message_ + POST /path HTTP/1.1\r + Transfer-Encoding: chunked\r + \r + 01x1\r + \r + 1 + _end_of_message_ + msg.gsub!(/^ {6}/, "") + req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) + req.parse(StringIO.new(msg)) + assert_raise(WEBrick::HTTPStatus::BadRequest){ req.body } + + # chunked req.body_reader + req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) + req.parse(StringIO.new(msg)) + dst = StringIO.new + assert_raise(WEBrick::HTTPStatus::BadRequest) do + IO.copy_stream(req.body_reader, dst) + end + end + + def test_null_byte_in_header + msg = <<-_end_of_message_ + POST /path HTTP/1.1\r + Evil: evil\x00\r + \r + _end_of_message_ + msg.gsub!(/^ {6}/, "") + req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) + assert_raise(WEBrick::HTTPStatus::BadRequest){ req.parse(StringIO.new(msg)) } + end + def test_forwarded msg = <<-_end_of_message_ GET /foo HTTP/1.1