18

A cookie associated with a cross-site resource at https://example.com/ was set without the SameSite attribute. It has been blocked, as Chrome now only delivers cookies with cross-site requests if they are set with SameSite=None and Secure. You can review cookies in developer tools under Application>Storage>Cookies and see more details at https://www.chromestatus.com/feature/5088147346030592 and https://www.chromestatus.com/feature/5633521622188032.

Please let me know how to set the SameSite cookie attribute. Thanks in advance.

Suresh Kumar
  • 169
  • 1
  • 1
  • 4
  • Can you please post some more context about the scenario where you are getting this warning in Chrome console. Is it a part of your code or some 3rd party library that is trying to set a cookie ? – Alok Swain Dec 05 '19 at 13:48
  • We have set of appications(more than 3). All the applications are embeded as a iframe in an applicaiton and able to access using the secure token and works fine on all the browsers. But, the mentioned issue is coming in "Google Chrome Canary" and not able to access the embedded appliations. For that, I Plan to add SameSite cookie. – Suresh Kumar Dec 05 '19 at 17:18

6 Answers6

17

In Rails 6.0 and 6.1 the same_site attribute has been added:

cookies["foo"] = {
  value: "bar",
  secure: Rails.application.config.secure_cookies,
  same_site: "None"
}

For Rails 5.x and lower, the rails_same_site_cookie gem is a good option for adding SameSite=None; to all your app's cookies. It uses middleware to do it.

Kelsey Hannan
  • 2,857
  • 2
  • 30
  • 46
  • 4
    Setting `cookies["foo"] = {value: "bar", same_site: :none, secure: true}` worked for me in Rails 5.2.3, though apparently [you need Rack >= 2.1.0](https://github.com/rails/rails/pull/28297#issuecomment-600566751) – Yarin Sep 08 '20 at 15:06
7

The way to set custom headers is to add the line below to your controller action:

response.headers['Set-Cookie'] = 'Secure;SameSite=None'.

Zeeshan Hassan Memon
  • 8,105
  • 4
  • 43
  • 57
Alok Swain
  • 6,409
  • 5
  • 36
  • 57
  • 12
    Please **do not** blanket add `SameSite=None` to all your cookies. This explicitly marks your cookies as being for cross-site delivery and is probably not what you want. You lose the security benefits of the CSRF protection that the new defaults provide. Consider adding `SameSite=Lax` as your default and **only** use `SameSite=None` when you **need** a cross-site cookie. More information on https://web.dev/samesite-cookies-explained – rowan_m May 15 '20 at 09:50
5

Action dispatch cookies is responsible for writing cookies to browser set in application, this uses Rack::Utils.set_cookie_header!.

Support for SameSite has been added after rack version 1.6, you need to check your rack version in Gemfile and if it is < 1.6 you need to add the following code in config/initializers

require 'rack/utils'
module Rack
  module Utils
    def self.set_cookie_header!(header, key, value)
      case value
      when Hash
        domain  = "; domain="  + value[:domain] if value[:domain]
        path    = "; path="    + value[:path]   if value[:path]
        max_age = "; max-age=" + value[:max_age] if value[:max_age]
        expires = "; expires=" +
          rfc2822(value[:expires].clone.gmtime) if value[:expires]
        secure = "; secure"  if value[:secure]
        httponly = "; HttpOnly" if value[:httponly]
        same_site =
          case value[:same_site]
          when false, nil
            nil
          when :none, 'None', :None
            '; SameSite=None'
          when :lax, 'Lax', :Lax
            '; SameSite=Lax'
          when true, :strict, 'Strict', :Strict
            '; SameSite=Strict'
          else
            raise ArgumentError, "Invalid SameSite value: #{value[:same_site].inspect}"
          end
        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}#{same_site}"

      case header["Set-Cookie"]
      when nil, ''
        header["Set-Cookie"] = cookie
      when String
        header["Set-Cookie"] = [header["Set-Cookie"], cookie].join("\n")
      when Array
        header["Set-Cookie"] = (header["Set-Cookie"] + [cookie]).join("\n")
      end

      nil
    end
  end
end

Once done you can set SameSite attribute while creating a new cookie, for ex:

cookies['testing'] = {
  value: 'test',
  path: '/',
  expiry: 1.weeks.from_now,
  same_site: :none
}

you can also add the same_site: <value> to your session store as well.

Hope this helps!

anil.n
  • 509
  • 2
  • 5
  • 17
  • I don't see SameSite=None in rack 1.6.11 implementation, so I believe we need to monkey patch that version too? – Nathan B Apr 02 '20 at 07:31
  • yes, if the support is not present we need to add the patch. – anil.n Oct 16 '20 at 08:07
  • Just FYI, looks like the earliest rack version that supports setting SameSite=None is 2.1.0: https://github.com/rack/rack/commit/c859bbf7b53cb59df1837612a8c330dfb4147392, while `Lax` or `Secure` is supported in 2.0.0: https://github.com/rack/rack/commit/f0f828cc1499cf54495e545daecb992a21fef324 – Hannele Sep 08 '21 at 17:26
1

What I did is the following: In session_store.rb I configured:

MyApp::Application.config.session_store :cache_store, key: COOKIE_NAME, :expire_after => 1.days, :expires_in => 1.days, :domain => 'mydomain.com', same_site: :none

And I added this monkey patch:

require 'rack/utils'
module Rack
  module Utils
    def self.set_cookie_header!(header, key, value)
      case value
      when Hash
        domain  = "; domain="  + value[:domain] if value[:domain]
        path    = "; path="    + value[:path]   if value[:path]
        max_age = "; max-age=" + value[:max_age] if value[:max_age]
        expires = "; expires=" +
            rfc2822(value[:expires].clone.gmtime) if value[:expires]

        # Make it always secure, even in HTTP
        # secure = "; secure"  if value[:secure]
        secure = "; secure"

        httponly = "; HttpOnly" if value[:httponly]
        same_site =
            case value[:same_site]
            when false, nil
              nil
            when :none, 'None', :None
              '; SameSite=None'
            when :lax, 'Lax', :Lax
              '; SameSite=Lax'
            when true, :strict, 'Strict', :Strict
              '; SameSite=Strict'
            else
              raise ArgumentError, "Invalid SameSite value: #{value[:same_site].inspect}"
            end
        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}#{same_site}"

      case header["Set-Cookie"]
      when nil, ''
        header["Set-Cookie"] = cookie
      when String
        header["Set-Cookie"] = [header["Set-Cookie"], cookie].join("\n")
      when Array
        header["Set-Cookie"] = (header["Set-Cookie"] + [cookie]).join("\n")
      end

      nil
    end
  end
end
Nathan B
  • 1,625
  • 1
  • 17
  • 15
-1

A backport of fixes to rack was contributed here.

https://github.com/rack/rack/pull/1547

It won't be released, so you'll likely need to use a fork of rack in your Gemfile.

https://bundler.io/v1.17/guides/git.html

ashawley
  • 4,195
  • 1
  • 27
  • 40
-1

Rails 6.0:

cookies[:foo] = {
    value: "bar",
    secure: true,
    same_site: :none
}

You gotta take care when you are testing it cause it will only works on production/staging environment when you have the HTTPS running. You wont be able to test it on development.

Lucas P
  • 9
  • 4