secret
Created

  • Download Gist
gistfile1.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
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("/",

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.