Skip to content

Commit

Permalink
Validate the SameSite cookie option
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
bobjflong authored and jeremy committed Apr 18, 2016
1 parent 0c74848 commit f0f828c
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 13 deletions.
17 changes: 13 additions & 4 deletions 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>

Expand Down
13 changes: 8 additions & 5 deletions lib/rack/utils.rb
Expand Up @@ -248,14 +248,17 @@ def add_cookie_to_header(header, key, value)
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
Expand Down
46 changes: 42 additions & 4 deletions test/spec_response.rb
Expand Up @@ -115,18 +115,56 @@
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}
Expand Down

0 comments on commit f0f828c

Please sign in to comment.