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 18:37:10 -0700
commit6712b864d574b7e09bf365cde3666e2da78b51b4 (patch)
treea63c1f42b04be15a859158451dd4ba0e6a3a82b1
parent4116309080b766daeb8c7c3fe52b14deea768b4f (diff)
downloadrack-6712b864d574b7e09bf365cde3666e2da78b51b4.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.md16
-rw-r--r--lib/rack/utils.rb13
-rw-r--r--test/spec_response.rb46
3 files changed, 64 insertions, 11 deletions
diff --git a/HISTORY.md b/HISTORY.md
index 2d3a8e36..6ed10cae 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,6 +1,18 @@
-Tue Mar 15 15:18:44 2016 Ben Toews <mastahyeti@users.noreply.github.com>
+Sun Dec 4 18:48:03 2015  Jeremy Daer <jeremydaer@gmail.com>
 
-        * Backport support for the `SameSite` cookie attribute.
+        * 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.
 
 Wed Jun 24 12:13:37 2015  Aaron Patterson <tenderlove@ruby-lang.org>
 
diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb
index 328f6554..f8e1544c 100644
--- a/lib/rack/utils.rb
+++ b/lib/rack/utils.rb
@@ -311,14 +311,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 bca892d6..4a5ad9b5 100644
--- a/test/spec_response.rb
+++ b/test/spec_response.rb
@@ -97,18 +97,56 @@ describe Rack::Response do
     response["Set-Cookie"].should.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"].should.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"].should.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}