はじめに
以下の記事にあるとおり、Chrome 80では2020年2月17日の週以降にデフォルトのSameSite属性が変更されます。
Chrome 80が密かに呼び寄せる地獄 ~ SameSite属性のデフォルト変更を調べてみた - Qiita
この変更が入ると、次のように「決済や認証などで外部サービスを利用し、外部サイトからPOSTで戻ってくるサイト」でユーザーが識別できないエラーが発生します。
(画像の引用元:Chrome 80が密かに呼び寄せる地獄 ~ SameSite属性のデフォルト変更を調べてみた - Qiita)
本記事ではRailsアプリケーションでこの問題に対応する方法を紹介します。
免責事項 (disclaimer)
この記事のとおりにあなたのRailsアプリケーションを変更して、何らかの不具合やセキュリティ上の問題が発生しても筆者は一切の責任を負いません。
技術的な背景と最新の技術情報を十分理解した上で、本記事の内容を適用してください。
(適用する必要がない場合は何もしないでください)
対応方法
rackのバージョンを2.1.0以上に上げます。
(2.1.0以上でないと、SameSite=None
属性に対応していないため - 参考)
(訂正:rails_same_site_cookieはrackの機能ではなく、独自にCookieを設定しているため、必ずしもrack 2.1.0以上に上げる必要はないようです)
rails_same_site_cookie gemをインストールします。
gem 'rails_same_site_cookie'
$ bundle install
対応は以上です。
Rails 6.1以上の場合(2022.5.27追記)
Rails 6.1からはSameSite=Lax
がデフォルト値になった関係でgemをインストールするだけでは機能しなくなりました(参考)。
Rails 6.1以降でも引き続き使う場合は、config/initializers
などで以下の設定も追加する必要があります。
Rails.application.configure do
config.action_dispatch.cookies_same_site_protection = nil
end
rails_same_site_cookie gemがやってくれること
rails_same_site_cookie
gemをインストールすると、自動的に全cookieにSameSite=None; Secure
属性が追加されます。
ただし、iOS 12とmacOS 10.14のSafariなど、SameSite=None; Secure
属性を付けると不具合が発生するブラウザ(参考)に対してはこの属性を付与しません。
rails_same_site_cookie gemがやってくれないこと
rails_same_site_cookie gemはRails側のCookieを変更するだけで、JavaScript側で設定するCookieには何も変更を加えません。
もしJS内でCookieを設定しているコードがある場合は、何らかの対応が必要になります。(詳細未調査)
参考1:動作確認の手順(例)
「決済で外部サービスを利用し、外部サイトからPOSTで戻ってくるサイト」を想定した場合の確認手順です。
いきなり本番環境で試すのではなく、テスト環境(ステージング環境)で試すようにしてください。
- こちらの手順に従って、Chromeの設定を変更する
- Railsアプリサイト上で商品を購入し、外部の決済サービスに遷移する
- 2分以上待つ(2分以上待たないとChromeがCookieを送信してしまうため)
- 決済を実行して、自サイトに戻る
- ログイン情報が維持されたまま、正常に決済が完了することを確認する(修正前は何らかの不具合が発生することも事前に確認しておく)
参考2:リクエストスペックでテストを書く(例)
SameSite=None
属性が適切に付与されているかどうかを確認するリクエストスペックの記述例です。
(非SSLで接続する場合、rails_same_site_cookie gemはSameSite=None
属性だけを付与し、Secure
属性は付与しません)
ログイン処理はDeviseで実現されていることを前提とします。
テストコード内の_your_app_session
は、適宜ご自身のアプリケーション名に合わせて変更してください。
require 'rails_helper'
RSpec.describe 'Cookies', type: :request do
describe 'Cookie の SameSite 属性' do
before do
user = create :user
login_as user
end
it 'User-Agent指定無しの場合 SameSite=None がつく' do
get new_user_session_path
expect(response.headers['Set-Cookie']).to match /_your_app_session=.*SameSite=None/
end
it 'SameSite=Lax がデフォルトになる Chrome 80 では SameSite=None がつく' do
mac_chrome_80 = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.42 Safari/537.36 '
win_chrome_80 = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.16 Safari/537.36'
get new_user_session_path, headers: { 'User-Agent' => mac_chrome_80 }
expect(response.headers['Set-Cookie']).to match /_your_app_session=.*SameSite=None/
get new_user_session_path, headers: { 'User-Agent' => win_chrome_80 }
expect(response.headers['Set-Cookie']).to match /_your_app_session=.*SameSite=None/
end
it 'SameSite=Noneの扱いにバグがある iOS12 Safari では SameSite=None がつかない' do
iphone_ios12_user_agent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1'
get new_user_session_path, headers: { 'User-Agent' => iphone_ios12_user_agent }
expect(response.headers['Set-Cookie']).to include '_your_app_session='
expect(response.headers['Set-Cookie']).not_to include 'SameSite'
end
end
end
参考文献
本記事を書くにあたって、下記記事を参考にさせてもらいました。
どうもありがとうございました。