summary refs log tree commit
diff options
context:
space:
mode:
authorBob Long <robertjflong@gmail.com>2016-04-16 21:48:54 +0100
committerJeremy Daer <jeremydaer@gmail.com>2016-04-17 17:58:15 -0700
commitf0f828cc1499cf54495e545daecb992a21fef324 (patch)
tree6f68290a166e0c6e7888060053acf9884332a2bc
parent0c748485a93fcf806741e8afd4dcc10603bbcfcb (diff)
downloadrack-f0f828cc1499cf54495e545daecb992a21fef324.tar.gz
Validate the SameSite cookie option
The draft spec for the SameSite option mentions two configuration
options: Strict & Lax. This commit introduces validation of the
associated same_site attribute.

The main motivation for validating this value is ensuring that awry
option values don't cause unexpected behaviour. As this is a sensitive
security option, I think validation is warranted.

The main drawback of validating the option value is that Rack won't
immediately support new options.

Signed-off-by: Jeremy Daer <jeremydaer@gmail.com>
-rw-r--r--HISTORY.md17
-rw-r--r--lib/rack/utils.rb13
-rw-r--r--test/spec_response.rb46
3 files changed, 63 insertions, 13 deletions
diff --git a/HISTORY.md b/HISTORY.md
index 270a7ba1..406d1758 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,9 +1,18 @@
 Sun Dec 4 18:48:03 2015  Jeremy Daer <jeremydaer@gmail.com>
 
-        * "First-Party" cookies. Browsers omit First-Party cookies from
-        third-party requests, closing the door on many common CSRF attacks.
-        Pass `first_party: true` to enable:
-            response.set_cookie 'foo', value: 'bar', first_party: true
+        * First-party "SameSite" cookies. Browsers omit SameSite cookies
+        from third-party requests, closing the door on many CSRF attacks.
+
+        Pass `same_site: true` (or `:strict`) to enable:
+            response.set_cookie 'foo', value: 'bar', same_site: true
+        or `same_site: :lax` to use Lax enforcement:
+            response.set_cookie 'foo', value: 'bar', same_site: :lax
+
+        Based on version 7 of the Same-site Cookies internet draft:
+        https://tools.ietf.org/html/draft-west-first-party-cookies-07
+
+        Thanks to Ben Toews (@mastahyeti) and Bob Long (@bobjflong) for
+        updating to drafts 5 and 7.
 
 Tue Nov  3 16:17:26 2015  Aaron Patterson <tenderlove@ruby-lang.org>
 
diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb
index 1aee9d34..74dc1de5 100644
--- a/lib/rack/utils.rb
+++ b/lib/rack/utils.rb
@@ -248,14 +248,17 @@ module Rack
           rfc2822(value[:expires].clone.gmtime) if value[:expires]
         secure = "; secure"  if value[:secure]
         httponly = "; HttpOnly" if (value.key?(:httponly) ? value[:httponly] : value[:http_only])
-        same_site = if value[:same_site]
+        same_site =
           case value[:same_site]
-          when Symbol, String
-            "; SameSite=#{value[:same_site]}"
+          when false, nil
+            nil
+          when :lax, 'Lax', :Lax
+            '; SameSite=Lax'.freeze
+          when true, :strict, 'Strict', :Strict
+            '; SameSite=Strict'.freeze
           else
-            "; SameSite"
+            raise ArgumentError, "Invalid SameSite value: #{value[:same_site].inspect}"
           end
-        end
         value = value[:value]
       end
       value = [value] unless Array === value
diff --git a/test/spec_response.rb b/test/spec_response.rb
index 70d81590..02e51435 100644
--- a/test/spec_response.rb
+++ b/test/spec_response.rb
@@ -115,18 +115,56 @@ describe Rack::Response do
     response["Set-Cookie"].must_equal "foo=bar"
   end
 
-  it "can set SameSite cookies with any truthy value" do
+  it "can set SameSite cookies with symbol value :lax" do
     response = Rack::Response.new
-    response.set_cookie "foo", {:value => "bar", :same_site => Object.new}
-    response["Set-Cookie"].must_equal "foo=bar; SameSite"
+    response.set_cookie "foo", {:value => "bar", :same_site => :lax}
+    response["Set-Cookie"].must_equal "foo=bar; SameSite=Lax"
   end
 
-  it "can set SameSite cookies with string value" do
+  it "can set SameSite cookies with symbol value :Lax" do
+    response = Rack::Response.new
+    response.set_cookie "foo", {:value => "bar", :same_site => :lax}
+    response["Set-Cookie"].must_equal "foo=bar; SameSite=Lax"
+  end
+
+  it "can set SameSite cookies with string value 'Lax'" do
     response = Rack::Response.new
     response.set_cookie "foo", {:value => "bar", :same_site => "Lax"}
     response["Set-Cookie"].must_equal "foo=bar; SameSite=Lax"
   end
 
+  it "can set SameSite cookies with boolean value true" do
+    response = Rack::Response.new
+    response.set_cookie "foo", {:value => "bar", :same_site => true}
+    response["Set-Cookie"].must_equal "foo=bar; SameSite=Strict"
+  end
+
+  it "can set SameSite cookies with symbol value :strict" do
+    response = Rack::Response.new
+    response.set_cookie "foo", {:value => "bar", :same_site => :strict}
+    response["Set-Cookie"].must_equal "foo=bar; SameSite=Strict"
+  end
+
+  it "can set SameSite cookies with symbol value :Strict" do
+    response = Rack::Response.new
+    response.set_cookie "foo", {:value => "bar", :same_site => :Strict}
+    response["Set-Cookie"].must_equal "foo=bar; SameSite=Strict"
+  end
+
+  it "can set SameSite cookies with string value 'Strict'" do
+    response = Rack::Response.new
+    response.set_cookie "foo", {:value => "bar", :same_site => "Strict"}
+    response["Set-Cookie"].must_equal "foo=bar; SameSite=Strict"
+  end
+
+  it "validates the SameSite option value" do
+    response = Rack::Response.new
+    lambda {
+      response.set_cookie "foo", {:value => "bar", :same_site => "Foo"}
+    }.must_raise(ArgumentError).
+      message.must_match(/Invalid SameSite value: "Foo"/)
+  end
+
   it "can set SameSite cookies with symbol value" do
     response = Rack::Response.new
     response.set_cookie "foo", {:value => "bar", :same_site => :Strict}