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

前端 未结 5 616
逝去的感伤
逝去的感伤 2021-01-05 02:31

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

相关标签:
5条回答
  • 2021-01-05 02:58

    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!

    0 讨论(0)
  • 2021-01-05 03:05

    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.

    0 讨论(0)
  • 2021-01-05 03:23

    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

    0 讨论(0)
  • 2021-01-05 03:24

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

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

    0 讨论(0)
  • 2021-01-05 03:24

    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
    
    0 讨论(0)
提交回复
热议问题