On February 4th 2020,则Google Chrome浏览器将要求将SameSite=None;
添加到所有跨站点Cookie中。 Rails cookie哈希的Rails 6.1 and soon Rails 6.0 have added a same_site: :none
选项:
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。
This StackOverflow answer显示了一种可修改Cookie的方法,以更新Rack Middleware中的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中?
答案 0 :(得分:4)
默认情况下,我能够使所有Cookie都使用SameSite=None
来更新机架:
gem 'rack', '~> 2.1'
use Rack::Session::Cookie,
:httponly => true,
:same_site => :none,
:secure => true,
:secret => COOKIE_SECRET.to_s()
答案 1 :(得分:3)
我能够将其与以下各项配合使用:
# 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)
答案 2 :(得分:2)
我遇到了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)
答案 3 :(得分:1)
更新:对于Rails 5.x及更低版本,我发现rails_same_site_cookie
gem是将SameSite=None;
添加到所有应用程序cookie中的不错选择。它使用中间件来实现。
答案 4 :(得分: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
}
}
我使用此解决方案在Rails 5应用程序的Cookie中添加SameSite = None。
答案 5 :(得分:0)
设置Cookie后,您将无法修改Cookie属性,例如expiry
,domain
,path
。
一旦设置了cookie,浏览器将仅返回cookie名称和值,覆盖任何cookie属性将创建一个新cookie。我建议删除现有的cookie并创建一个具有相同名称和值的新cookie。
headers['Set-Cookie']
指示浏览器创建一个新的cookie,并且在中间件中修改值使您几乎无法控制属性值。
我已经回答here,如何通过修改Rack::Utils.set_cookie_header!
方法来实现。