How to set SameSite attribute to 'None; Secure' in Rails3.1.12 and Ruby1.9.3
Asked Answered
H

6

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.

Horbal answered 5/12, 2019 at 12:32 Comment(2)
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 ?Haversack
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.Horbal
F
18

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.

Frisk answered 27/1, 2020 at 22:1 Comment(1)
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.0Sos
H
7

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

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

Haversack answered 5/12, 2019 at 13:48 Comment(1)
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 web.dev/samesite-cookies-explainedHynes
G
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!

Goddard answered 3/2, 2020 at 9:33 Comment(3)
I don't see SameSite=None in rack 1.6.11 implementation, so I believe we need to monkey patch that version too?Achromaticity
yes, if the support is not present we need to add the patch.Goddard
Just FYI, looks like the earliest rack version that supports setting SameSite=None is 2.1.0: github.com/rack/rack/commit/…, while Lax or Secure is supported in 2.0.0: github.com/rack/rack/commit/…Diophantus
A
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
Achromaticity answered 2/4, 2020 at 11:22 Comment(0)
I
-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

Issus answered 3/2, 2020 at 14:50 Comment(0)
E
-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.

Eriha answered 9/3, 2021 at 15:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.