diff options
author | Bob Long <robertjflong@gmail.com> | 2016-04-16 21:48:54 +0100 |
---|---|---|
committer | Jeremy Daer <jeremydaer@gmail.com> | 2016-04-17 17:58:15 -0700 |
commit | f0f828cc1499cf54495e545daecb992a21fef324 (patch) | |
tree | 6f68290a166e0c6e7888060053acf9884332a2bc | |
parent | 0c748485a93fcf806741e8afd4dcc10603bbcfcb (diff) | |
download | rack-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.md | 17 | ||||
-rw-r--r-- | lib/rack/utils.rb | 13 | ||||
-rw-r--r-- | test/spec_response.rb | 46 |
3 files changed, 63 insertions, 13 deletions
@@ -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} |