10

2020 年 2 月 4 日,Google Chrome 将需要SameSite=None;添加到所有跨站点 cookie。 Rails 6.1 和即将发布的 Rails 6.0same_site: :none为 rails cookie 哈希添加了一个选项:

cookies["foo"]= {
  value: "bar",
  expires: 1.year.from_now,
  same_site: :none
} 

但较旧的 Rails 5.x 应用程序不会获得升级以访问same_site选项哈希。我知道SameSite=None;cookie 选项可以手动添加到控制器中的 Rails 使用:

response.headers["Set-Cookie"] = "my=cookie; path=/; expires=#{1.year.from_now}; SameSite=None;"

但是我的 Rails 5.x 应用程序使用复杂的 cookie 对象来修改 cookie。我不想将它们分开,而是想编写 Rack 中间件来SameSite=None;一次手动更新所有带有该属性的 cookie。

这个 StackOverflow 答案显示了一种可以修改 cookie 以更新机架中间件中的 cookie 的方法:

# lib/same_site_cookie_middleware
class SameSiteCookieMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)
    # confusingly, response takes its args in a different order
    # than rack requires them to be passed on
    # I know it's because most likely you'll modify the body, 
    # and the defaults are fine for the others. But, it still bothers me.

    response = Rack::Response.new body, status, headers

    response.set_cookie("foo", {:value => "bar", :path => "/", :expires => 1.year.from_now, same_site: :none})
    response.finish # finish writes out the response in the expected format.
  end
end
# application.rb
require 'same_site_cookie_middleware'
config.middleware.insert_after(ActionDispatch::Cookies, SameSiteCookieMiddleware)

如何重新编写此机架中间件代码以手动附加SameSite=None;到每个现有的 cookie 中?

4

6 回答 6

4

默认情况下,我能够让所有 cookie 使用SameSite=None更新机架:

gem 'rack', '~> 2.1'

use Rack::Session::Cookie, 
        :httponly     => true,
        :same_site    => :none,
        :secure       => true,
        :secret       => COOKIE_SECRET.to_s()
于 2020-03-06T04:11:03.127 回答
4

我能够使它与以下内容一起使用:

# frozen_string_literals: true

class SameSiteCookies

  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)

    set_cookie_header = headers['Set-Cookie']

    if set_cookie_header && !(set_cookie_header =~ /SameSite\=/)

      headers['Set-Cookie'] << ';' if !(set_cookie_header =~ /;$/)
      headers['Set-Cookie'] << ' SameSite=None'
      headers['Set-Cookie'] << '; Secure' if env['rack.url_scheme'] == 'https';

    end

    [status, headers, body]
  end
end

并添加到中间件:

Rails.application.config.middleware.insert_before(ActionDispatch::Cookies, SameSiteCookies)
于 2020-01-31T21:03:36.167 回答
3

我遇到了 Rails 5 标题被冻结的问题。这类似于卡森的回答,但它解决了这个问题。应该适用于 rails 5 < 和 Rails 5+。

# frozen_string_literals: true

class SameSiteCookies

  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)

    set_cookie_header = headers['Set-Cookie']

    if set_cookie_header && !(set_cookie_header =~ /SameSite\=/)
      # the set cookie header variable is frozen
      new_set_cookie_header = set_cookie_header.dup
      new_set_cookie_header << ';' if !(set_cookie_header =~ /;$/)
      new_set_cookie_header << ' SameSite=None'
      new_set_cookie_header << '; Secure' if is_ssl?

      headers['Set-Cookie'] = new_set_cookie_header

    end

    [status, headers, body]
  end

  private

  def is_ssl?
    # custom logic for my application
  end
end

插入中间件

Rails.application.config.middleware.insert_before(ActionDispatch::Cookies, SameSiteCookies)
于 2020-03-27T13:14:17.357 回答
1

secure_headers gem可以让你配置开箱即用的 cookie 策略:

SecureHeaders::Configuration.default do |config|
  config.cookies = {
    secure: true, # mark all cookies as "Secure"
    httponly: true, # mark all cookies as "HttpOnly"
    samesite: {
      none: true # mark all cookies as SameSite=lax
    }
  }

我使用此解决方案将 SameSite=None 添加到 Rails 5 应用程序的 cookie 中。

于 2020-08-20T18:41:23.343 回答
1

更新:对于 Rails 5.x 及更低版本,我发现rails_same_site_cookiegem 是添加SameSite=None;到所有应用程序 cookie 的好选择。它使用中间件来做到这一点。

于 2020-02-03T20:28:35.780 回答
0

设置 cookie 后,您无法修改 cookie 属性,如expirydomainpath

一旦设置了 cookie,浏览器只返回 cookie 名称和值,覆盖任何 cookie 属性将创建一个新的 cookie。我建议删除现有的 cookie 并创建一个具有相同名称和值的新 cookie。

headers['Set-Cookie']指示浏览器创建一个新的 cookie 并修改中间件中的值可以让您对属性值进行很少的控制。

我在这里回答了如何通过修改Rack::Utils.set_cookie_header!方法来实现。

于 2020-02-03T10:11:26.240 回答