merge revision(s) 62968:

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/branches/ruby_2_2@63022 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
usa 2018-03-28 14:50:27 +00:00
parent a45622669b
commit bbda1a0274
4 changed files with 60 additions and 3 deletions

View File

@ -1,3 +1,15 @@
Wed Mar 28 23:48:24 2018 Eric Wong <normalperson@yhbt.net>
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
Wed Mar 28 23:45:36 2018 Eric Wong <normalperson@yhbt.net>
webrick: use IO.copy_stream for multipart response

View File

@ -20,6 +20,8 @@ module WEBrick
# WEBrick HTTP Servlet.
class HTTPResponse
class InvalidHeader < StandardError
end
##
# HTTP Response version
@ -286,14 +288,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
_write_data(socket, data)
end
rescue InvalidHeader => e
@header.clear
@cookies.clear
set_error e
retry
end
##
@ -353,6 +360,22 @@ module WEBrick
host, port = @config[:ServerName], @config[:Port]
end
error_body(backtrace, ex, host, port)
end
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)
@body = ''
@body << <<-_end_of_html_
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">

View File

@ -1,6 +1,7 @@
require "webrick"
require "minitest/autorun"
require "stringio"
require "net/http"
module WEBrick
class TestHTTPResponse < MiniTest::Unit::TestCase
@ -27,6 +28,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

View File

@ -1,6 +1,6 @@
#define RUBY_VERSION "2.2.10"
#define RUBY_RELEASE_DATE "2018-03-28"
#define RUBY_PATCHLEVEL 488
#define RUBY_PATCHLEVEL 489
#define RUBY_RELEASE_YEAR 2018
#define RUBY_RELEASE_MONTH 3