summary refs log tree commit
diff options
context:
space:
mode:
authorJeremy Daer <jeremydaer@gmail.com>2015-12-04 19:07:53 -0700
committerBen Toews <mastahyeti@users.noreply.github.com>2016-03-17 13:37:54 -0600
commitcc21d02a7d0fa1df5008e70127a57ab6f1e38cb5 (patch)
treed6631884f07fd6febc6e572c28243435e3636239
parent2fd9df71aff4af8a3ab8088a6919f5d9a5e4ab95 (diff)
downloadrack-cc21d02a7d0fa1df5008e70127a57ab6f1e38cb5.tar.gz
First-Party cookies, another line of CSRF defense
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
-rw-r--r--lib/rack/utils.rb3
-rw-r--r--test/spec_response.rb14
2 files changed, 16 insertions, 1 deletions
diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb
index 3b6f69f3..ab36ed84 100644
--- a/lib/rack/utils.rb
+++ b/lib/rack/utils.rb
@@ -311,12 +311,13 @@ 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])
+        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}"
+        "#{domain}#{path}#{max_age}#{expires}#{secure}#{httponly}#{first_party}"
 
       case header["Set-Cookie"]
       when nil, ''
diff --git a/test/spec_response.rb b/test/spec_response.rb
index 6b13c0c9..c0354934 100644
--- a/test/spec_response.rb
+++ b/test/spec_response.rb
@@ -97,6 +97,20 @@ describe Rack::Response do
     response["Set-Cookie"].should.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"