webrick: prevent response splitting and header injection

Original patch by tenderlove (with minor style adjustments).

* lib/webrick/httpresponse.rb (send_header): call check_header
  (check_header): raise on embedded CRLF in header value
* test/webrick/test_httpresponse.rb
  (test_prevent_response_splitting_headers): new test
* (test_prevent_response_splitting_cookie_headers): ditto

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62968 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
normal 2018-03-28 08:38:26 +00:00
Родитель 7292569d3f
Коммит d9d4a28f1c
2 изменённых файлов: 39 добавлений и 2 удалений

Просмотреть файл

@ -21,6 +21,8 @@ module WEBrick
# WEBrick HTTP Servlet.
class HTTPResponse
class InvalidHeader < StandardError
end
##
# HTTP Response version
@ -287,14 +289,19 @@ module WEBrick
data = status_line()
@header.each{|key, value|
tmp = key.gsub(/\bwww|^te$|\b\w/){ $&.upcase }
data << "#{tmp}: #{value}" << CRLF
data << "#{tmp}: #{check_header(value)}" << CRLF
}
@cookies.each{|cookie|
data << "Set-Cookie: " << cookie.to_s << CRLF
data << "Set-Cookie: " << check_header(cookie.to_s) << CRLF
}
data << CRLF
socket.write(data)
end
rescue InvalidHeader => e
@header.clear
@cookies.clear
set_error e
retry
end
##
@ -359,6 +366,14 @@ module WEBrick
private
def check_header(header_value)
if header_value =~ /\r\n/
raise InvalidHeader
else
header_value
end
end
# :stopdoc:
def error_body(backtrace, ex, host, port)

Просмотреть файл

@ -2,6 +2,7 @@
require "webrick"
require "minitest/autorun"
require "stringio"
require "net/http"
module WEBrick
class TestHTTPResponse < MiniTest::Unit::TestCase
@ -28,6 +29,27 @@ module WEBrick
@res.keep_alive = true
end
def test_prevent_response_splitting_headers
res['X-header'] = "malicious\r\nCookie: hack"
io = StringIO.new
res.send_response io
io.rewind
res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io))
assert_equal '500', res.code
refute_match 'hack', io.string
end
def test_prevent_response_splitting_cookie_headers
user_input = "malicious\r\nCookie: hack"
res.cookies << WEBrick::Cookie.new('author', user_input)
io = StringIO.new
res.send_response io
io.rewind
res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io))
assert_equal '500', res.code
refute_match 'hack', io.string
end
def test_304_does_not_log_warning
res.status = 304
res.setup_header