Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
commit b71a96c2e2d223f5a9228ee06461ab47db6109b0
Author: Evan Phoenix <evan@fallingsnow.net>
Date: Tue Dec 13 10:18:48 2011 -0800
Limit the size of parameter keys
diff --git a/lib/rack/multipart/parser.rb b/lib/rack/multipart/parser.rb
index 6eee64e..2b55cf9 100644
--- a/lib/rack/multipart/parser.rb
+++ b/lib/rack/multipart/parser.rb
@@ -14,6 +14,9 @@ module Rack
fast_forward_to_first_boundary
+ max_key_space = Utils.key_space_limit
+ bytes = 0
+
loop do
head, filename, content_type, name, body =
get_current_head_and_filename_and_content_type_and_name_and_body
@@ -28,6 +31,13 @@ module Rack
filename, data = get_data(filename, body, content_type, name, head)
+ if name
+ bytes += name.size
+ if bytes > max_key_space
+ raise RangeError, "exceeded available parameter key space"
+ end
+ end
+
Utils.normalize_params(@params, name, data) unless data.nil?
# break if we're at the end of a buffer, but not if it is the end of a field
diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb
index 45f4b75..e29f1c8 100644
--- a/lib/rack/utils.rb
+++ b/lib/rack/utils.rb
@@ -47,6 +47,14 @@ module Rack
DEFAULT_SEP = /[&;] */n
+ class << self
+ attr_accessor :key_space_limit
+ end
+
+ # The default number of bytes to allow parameter keys to take up.
+ # This helps prevent a rogue client from flooding a Request.
+ self.key_space_limit = 65536
+
# Stolen from Mongrel, with some small modifications:
# Parses a query string by breaking it up at the '&'
# and ';' characters. You can also use this to parse
@@ -55,8 +63,19 @@ module Rack
def parse_query(qs, d = nil)
params = {}
+ max_key_space = Utils.key_space_limit
+ bytes = 0
+
(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
k, v = p.split('=', 2).map { |x| unescape(x) }
+
+ if k
+ bytes += k.size
+ if bytes > max_key_space
+ raise RangeError, "exceeded available parameter key space"
+ end
+ end
+
if cur = params[k]
if cur.class == Array
params[k] << v
@@ -75,8 +94,19 @@ module Rack
def parse_nested_query(qs, d = nil)
params = {}
+ max_key_space = Utils.key_space_limit
+ bytes = 0
+
(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
k, v = p.split('=', 2).map { |s| unescape(s) }
+
+ if k
+ bytes += k.size
+ if bytes > max_key_space
+ raise RangeError, "exceeded available parameter key space"
+ end
+ end
+
normalize_params(params, k, v)
end
diff --git a/test/spec_multipart.rb b/test/spec_multipart.rb
index 4ecd2de..1dc2f4d 100644
--- a/test/spec_multipart.rb
+++ b/test/spec_multipart.rb
@@ -30,6 +30,17 @@ describe Rack::Multipart do
params["text"].should.equal "contents"
end
+ should "raise RangeError if the key space is exhausted" do
+ env = Rack::MockRequest.env_for("/", multipart_fixture(:content_type_and_no_filename))
+
+ old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1
+ begin
+ lambda { Rack::Multipart.parse_multipart(env) }.should.raise(RangeError)
+ ensure
+ Rack::Utils.key_space_limit = old
+ end
+ end
+
should "parse multipart form webkit style" do
env = Rack::MockRequest.env_for '/', multipart_fixture(:webkit)
env['CONTENT_TYPE'] = "multipart/form-data; boundary=----WebKitFormBoundaryWLHCs9qmcJJoyjKR"
diff --git a/test/spec_request.rb b/test/spec_request.rb
index b7adfd7..6aa49f7 100644
--- a/test/spec_request.rb
+++ b/test/spec_request.rb
@@ -125,6 +125,18 @@ describe Rack::Request do
req.params.should.equal "foo" => "bar", "quux" => "bla"
end
+ should "limit the keys from the GET query string" do
+ env = Rack::MockRequest.env_for("/?foo=bar")
+
+ old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1
+ begin
+ req = Rack::Request.new(env)
+ lambda { req.GET }.should.raise(RangeError)
+ ensure
+ Rack::Utils.key_space_limit = old
+ end
+ end
+
should "not unify GET and POST when calling params" do
mr = Rack::MockRequest.env_for("/?foo=quux",
"REQUEST_METHOD" => 'POST',
@@ -157,6 +169,20 @@ describe Rack::Request do
req.params.should.equal "foo" => "bar", "quux" => "bla"
end
+ should "limit the keys from the POST form data" do
+ env = Rack::MockRequest.env_for("",
+ "REQUEST_METHOD" => 'POST',
+ :input => "foo=bar&quux=bla")
+
+ old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1
+ begin
+ req = Rack::Request.new(env)
+ lambda { req.POST }.should.raise(RangeError)
+ ensure
+ Rack::Utils.key_space_limit = old
+ end
+ end
+
should "parse POST data with explicit content type regardless of method" do
req = Rack::Request.new \
Rack::MockRequest.env_for("/",
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.