Adding 'SameSite=None;' cookies to Rails via Rack middleware?

别等时光非礼了梦想. 提交于 2020-04-13 03:59:49

问题


On February 4th 2020, Google Chrome will require SameSite=None; to be added to all cross-site cookies. Rails 6.1 and soon Rails 6.0 have added a same_site: :none option to the rails cookie hash:

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

But older Rails 5.x apps won't receive the upgrade to have access to the same_site options hash. I know the SameSite=None; cookie option can be manually added to Rails in a controller using:

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

But my Rails 5.x app uses complicated cookie objects that modify cookies. Instead of breaking them apart, I would like to write Rack middleware to manually update all cookies with the SameSite=None; attribute at once.

This StackOverflow answer shows a way to cookies can be modified to update cookies within Rack Middleware:

# 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)

How do I re-write this Rack Middleware code to manually append SameSite=None; into every existing cookie?


回答1:


I was able to get this to work with the following:

# 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

and adding to middleware with:

Rails.application.config.middleware.insert_before(ActionDispatch::Cookies, SameSiteCookies)



回答2:


I was able to get all cookies to use SameSite=None by default updating rack:

gem 'rack', '~> 2.1'

use Rack::Session::Cookie, 
        :httponly     => true,
        :same_site    => :none,
        :secure       => true,
        :secret       => COOKIE_SECRET.to_s()



回答3:


Once a cookie is set you cannot modify the cookie properties like expiry, domain, path.

Browsers return only the cookie name and value once a cookie has already been set, over-riding any of the cookie property will create a new cookie. I would recommend to delete the existing cookie and create a new cookie with same name and value.

headers['Set-Cookie'] instructs the browser to create a new cookie and modifying the value in middleware gives you a very little control on the attribute value.

I have answered here how this can be achieved by modifying the Rack::Utils.set_cookie_header! method.




回答4:


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




回答5:


I had a problem with Rails 5 headers being frozen. This is similar to Carson's answer but it goes around this problem. Should work for both rails 5 < and 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

Insert the middleware

Rails.application.config.middleware.insert_before(ActionDispatch::Cookies, SameSiteCookies)


来源:https://stackoverflow.com/questions/59870058/adding-samesite-none-cookies-to-rails-via-rack-middleware

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!