diff options
-rw-r--r-- | HISTORY.md | 16 | ||||
-rw-r--r-- | lib/rack/utils.rb | 13 | ||||
-rw-r--r-- | test/spec_response.rb | 46 |
3 files changed, 64 insertions, 11 deletions
@@ -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} |