summary refs log tree commit
diff options
context:
space:
mode:
authorPatrick Tulskie <patricktulskie@gmail.com>2018-08-22 12:45:30 -0400
committerAaron Patterson <aaron.patterson@gmail.com>2018-11-05 11:19:16 -0800
commit97ca63d87d88b4088fb1995b14103d4fe6a5e594 (patch)
tree9867fceb55df1ecc45cbdc140d182d55aed702aa
parent7b5054eedfdbd8f7dd5f348b0a02678b64fdd9de (diff)
downloadrack-97ca63d87d88b4088fb1995b14103d4fe6a5e594.tar.gz
Whitelist http/https schemes
[CVE-2018-16471]
-rw-r--r--lib/rack/request.rb21
-rw-r--r--test/spec_request.rb5
2 files changed, 22 insertions, 4 deletions
diff --git a/lib/rack/request.rb b/lib/rack/request.rb
index ac95b1ca..3c35671c 100644
--- a/lib/rack/request.rb
+++ b/lib/rack/request.rb
@@ -13,6 +13,8 @@ module Rack
     # The environment of the request.
     attr_reader :env
 
+    SCHEME_WHITELIST = %w(https http).freeze
+
     def initialize(env)
       @env = env
     end
@@ -68,10 +70,8 @@ module Rack
         'https'
       elsif @env['HTTP_X_FORWARDED_SSL'] == 'on'
         'https'
-      elsif @env['HTTP_X_FORWARDED_SCHEME']
-        @env['HTTP_X_FORWARDED_SCHEME']
-      elsif @env['HTTP_X_FORWARDED_PROTO']
-        @env['HTTP_X_FORWARDED_PROTO'].split(',')[0]
+      elsif forwarded_scheme
+        forwarded_scheme
       else
         @env["rack.url_scheme"]
       end
@@ -394,5 +394,18 @@ module Rack
         s
       end
     end
+
+    def forwarded_scheme
+      scheme_headers = [
+        @env['HTTP_X_FORWARDED_SCHEME'],
+        @env['HTTP_X_FORWARDED_PROTO'].to_s.split(',')[0]
+      ]
+
+      scheme_headers.each do |header|
+        return header if SCHEME_WHITELIST.include?(header)
+      end
+
+      nil
+    end
   end
 end
diff --git a/test/spec_request.rb b/test/spec_request.rb
index a44e0a71..8025613f 100644
--- a/test/spec_request.rb
+++ b/test/spec_request.rb
@@ -425,6 +425,11 @@ describe Rack::Request do
     request.should.be.ssl?
   end
 
+  should "prevent scheme abuse" do
+    request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_SCHEME' => 'a."><script>alert(1)</script>'))
+    request.scheme.should.not.equal 'a."><script>alert(1)</script>'
+  end
+
   should "parse cookies" do
     req = Rack::Request.new \
       Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m")