diff options
author | Bob Long <robertjflong@gmail.com> | 2016-04-16 21:48:54 +0100 |
---|---|---|
committer | Jeremy Daer <jeremydaer@gmail.com> | 2016-04-17 18:37:10 -0700 |
commit | 6712b864d574b7e09bf365cde3666e2da78b51b4 (patch) | |
tree | a63c1f42b04be15a859158451dd4ba0e6a3a82b1 | |
parent | 4116309080b766daeb8c7c3fe52b14deea768b4f (diff) | |
download | rack-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.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} |