Skip to content

Commit

Permalink
First-Party cookies, another line of CSRF defense
Browse files Browse the repository at this point in the history
Set `first_party: true` to set the First-Party attribute telling
browsers to only send the cookie with legit first-party requests.

* https://tools.ietf.org/html/draft-west-first-party-cookies-00
* https://www.chromestatus.com/feature/4672634709082112
  • Loading branch information
jeremy committed Dec 5, 2015
1 parent c393176 commit 159eb9b
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 1 deletion.
7 changes: 7 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
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

Tue Nov 3 16:17:26 2015 Aaron Patterson <tenderlove@ruby-lang.org>

* Add `Rack::Events` middleware for adding event based middleware:
Expand Down
3 changes: 2 additions & 1 deletion lib/rack/utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -248,12 +248,13 @@ 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])
first_party = "; First-Party" if value[:first_party]
value = value[:value]
end
value = [value] unless Array === value

cookie = "#{escape(key)}=#{value.map { |v| escape v }.join('&')}#{domain}" \
"#{path}#{max_age}#{expires}#{secure}#{httponly}"
"#{path}#{max_age}#{expires}#{secure}#{httponly}#{first_party}"

case header
when nil, ''
Expand Down
14 changes: 14 additions & 0 deletions test/spec_response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,20 @@
response["Set-Cookie"].must_equal "foo=bar"
end

it "can set First-Party cookies" do
response = Rack::Response.new
response.set_cookie "foo", {:value => "bar", :first_party => true}
response["Set-Cookie"].must_equal "foo=bar; First-Party"
end

[ nil, false ].each do |non_truthy|
it "omits First-Party attribute given a #{non_truthy.inspect} value" do
response = Rack::Response.new
response.set_cookie "foo", {:value => "bar", :first_party => non_truthy}
response["Set-Cookie"].must_equal "foo=bar"
end
end

it "can delete cookies" do
response = Rack::Response.new
response.set_cookie "foo", "bar"
Expand Down

0 comments on commit 159eb9b

Please sign in to comment.