|
|
121925 |
From 3f591af1e74ec511e38bd40afc9ebbceacdc9fef Mon Sep 17 00:00:00 2001
|
|
|
121925 |
From: usa <usa@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
|
|
|
121925 |
Date: Wed, 28 Mar 2018 14:50:27 +0000
|
|
|
121925 |
Subject: [PATCH] webrick: prevent response splitting and header injection
|
|
|
121925 |
|
|
|
121925 |
Original patch by tenderlove (with minor style adjustments).
|
|
|
121925 |
|
|
|
121925 |
* lib/webrick/httpresponse.rb (send_header): call check_header
|
|
|
121925 |
(check_header): raise on embedded CRLF in header value
|
|
|
121925 |
* test/webrick/test_httpresponse.rb
|
|
|
121925 |
(test_prevent_response_splitting_headers): new test
|
|
|
121925 |
* (test_prevent_response_splitting_cookie_headers): ditto
|
|
|
121925 |
|
|
|
121925 |
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_2@63022 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
|
|
|
121925 |
---
|
|
|
121925 |
lib/webrick/httpresponse.rb | 27 +++++++++++++++++++++++++--
|
|
|
121925 |
test/webrick/test_httpresponse.rb | 23 +++++++++++++++++++++++
|
|
|
121925 |
2 files changed, 48 insertions(+), 2 deletions(-)
|
|
|
121925 |
|
|
|
121925 |
diff --git a/lib/webrick/httpresponse.rb b/lib/webrick/httpresponse.rb
|
|
|
121925 |
index 8e3eb39a31..11cc78d845 100644
|
|
|
121925 |
--- a/lib/webrick/httpresponse.rb
|
|
|
121925 |
+++ b/lib/webrick/httpresponse.rb
|
|
|
121925 |
@@ -20,6 +20,8 @@ module WEBrick
|
|
|
121925 |
# WEBrick HTTP Servlet.
|
|
|
121925 |
|
|
|
121925 |
class HTTPResponse
|
|
|
121925 |
+ class InvalidHeader < StandardError
|
|
|
121925 |
+ end
|
|
|
121925 |
|
|
|
121925 |
##
|
|
|
121925 |
# HTTP Response version
|
|
|
121925 |
@@ -285,14 +287,19 @@ module WEBrick
|
|
|
121925 |
data = status_line()
|
|
|
121925 |
@header.each{|key, value|
|
|
|
121925 |
tmp = key.gsub(/\bwww|^te$|\b\w/){ $&.upcase }
|
|
|
121925 |
- data << "#{tmp}: #{value}" << CRLF
|
|
|
121925 |
+ data << "#{tmp}: #{check_header(value)}" << CRLF
|
|
|
121925 |
}
|
|
|
121925 |
@cookies.each{|cookie|
|
|
|
121925 |
- data << "Set-Cookie: " << cookie.to_s << CRLF
|
|
|
121925 |
+ data << "Set-Cookie: " << check_header(cookie.to_s) << CRLF
|
|
|
121925 |
}
|
|
|
121925 |
data << CRLF
|
|
|
121925 |
_write_data(socket, data)
|
|
|
121925 |
end
|
|
|
121925 |
+ rescue InvalidHeader => e
|
|
|
121925 |
+ @header.clear
|
|
|
121925 |
+ @cookies.clear
|
|
|
121925 |
+ set_error e
|
|
|
121925 |
+ retry
|
|
|
121925 |
end
|
|
|
121925 |
|
|
|
121925 |
##
|
|
|
121925 |
@@ -349,6 +356,22 @@ module WEBrick
|
|
|
121925 |
host, port = @config[:ServerName], @config[:Port]
|
|
|
121925 |
end
|
|
|
121925 |
|
|
|
121925 |
+ error_body(backtrace, ex, host, port)
|
|
|
121925 |
+ end
|
|
|
121925 |
+
|
|
|
121925 |
+ private
|
|
|
121925 |
+
|
|
|
121925 |
+ def check_header(header_value)
|
|
|
121925 |
+ if header_value =~ /\r\n/
|
|
|
121925 |
+ raise InvalidHeader
|
|
|
121925 |
+ else
|
|
|
121925 |
+ header_value
|
|
|
121925 |
+ end
|
|
|
121925 |
+ end
|
|
|
121925 |
+
|
|
|
121925 |
+ # :stopdoc:
|
|
|
121925 |
+
|
|
|
121925 |
+ def error_body(backtrace, ex, host, port)
|
|
|
121925 |
@body = ''
|
|
|
121925 |
@body << <<-_end_of_html_
|
|
|
121925 |
|
|
|
121925 |
diff --git a/test/webrick/test_httpresponse.rb b/test/webrick/test_httpresponse.rb
|
|
|
121925 |
index d5d5552796..bdf38e6b5c 100644
|
|
|
121925 |
--- a/test/webrick/test_httpresponse.rb
|
|
|
121925 |
+++ b/test/webrick/test_httpresponse.rb
|
|
|
121925 |
@@ -1,5 +1,7 @@
|
|
|
121925 |
require "webrick"
|
|
|
121925 |
require "minitest/autorun"
|
|
|
121925 |
+require "stringio"
|
|
|
121925 |
+require "net/http"
|
|
|
121925 |
|
|
|
121925 |
module WEBrick
|
|
|
121925 |
class TestHTTPResponse < MiniTest::Unit::TestCase
|
|
|
121925 |
@@ -26,6 +28,27 @@ module WEBrick
|
|
|
121925 |
@res.keep_alive = true
|
|
|
121925 |
end
|
|
|
121925 |
|
|
|
121925 |
+ def test_prevent_response_splitting_headers
|
|
|
121925 |
+ res['X-header'] = "malicious\r\nCookie: hack"
|
|
|
121925 |
+ io = StringIO.new
|
|
|
121925 |
+ res.send_response io
|
|
|
121925 |
+ io.rewind
|
|
|
121925 |
+ res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io))
|
|
|
121925 |
+ assert_equal '500', res.code
|
|
|
121925 |
+ refute_match 'hack', io.string
|
|
|
121925 |
+ end
|
|
|
121925 |
+
|
|
|
121925 |
+ def test_prevent_response_splitting_cookie_headers
|
|
|
121925 |
+ user_input = "malicious\r\nCookie: hack"
|
|
|
121925 |
+ res.cookies << WEBrick::Cookie.new('author', user_input)
|
|
|
121925 |
+ io = StringIO.new
|
|
|
121925 |
+ res.send_response io
|
|
|
121925 |
+ io.rewind
|
|
|
121925 |
+ res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io))
|
|
|
121925 |
+ assert_equal '500', res.code
|
|
|
121925 |
+ refute_match 'hack', io.string
|
|
|
121925 |
+ end
|
|
|
121925 |
+
|
|
|
121925 |
def test_304_does_not_log_warning
|
|
|
121925 |
res.status = 304
|
|
|
121925 |
res.setup_header
|
|
|
121925 |
--
|
|
|
121925 |
2.17.1
|
|
|
121925 |
|