何でも屋エンジニアのブログ

ソフトウェア関連技術、コミュニティ、日々の雑貨

開発環境でActionController::InvalidAuthenticityTokenが出るようになった

結論

SameSite=None; Secureにしているサービスは開発環境もHTTPS化しておきましょうというお話です。Chromeは84よりデフォルトの挙動が変わり、FirefoxFirefox 69からSameSite属性が利用できるようになっています(デフォルトの挙動はまだのよう)。

自分の担当しているサービスでもメンバーが既にHTTPS化してくれていましたが、HTTPとHTTPS両方使える状態だったので今回の事象に遭遇してしまいました。

経緯

初回リクエスト時のレスポンスを返す際に生成したCSRFトークンがsession[:_csrf_token]に格納され、次のリクエスト時には当該データとリクエスト時に受け取ったCSRFトークンを突き合わせリクエストの整合性を担保している。

具体的にはこのあたり。

def real_csrf_token(session) # :doc:
  session[:_csrf_token] ||= SecureRandom.base64(AUTHENTICITY_TOKEN_LENGTH)
  Base64.strict_decode64(session[:_csrf_token])
end

出典: actionpack/lib/action_controller/metal/request_forgery_protection.rb

しかし2回目のリクエスト時にはその前に生成されているはずのCSRFトークンがsession[:_csrf_token]に格納されておらず、不正なリクエストと判定されているようだった。Safariで確認したところ想定通りのタイミングでsession[:_csrf_token]に値が格納されていた。

自分で挙動を調査しチームメンバーにヒアリングしたところ

  • HTTPSでログイン可能(上記不具合はHTTPによるアクセス)
  • Chromeではログインできる人とできない人がいる(HTTP)
  • Chrome以外のブラウザではログイン可能(HTTP)

という状態だった。

HTTPSでログイン可能という挙動とデバッグの過程から、SameSite Cookiesの機能ではないかと疑った。 Chromeでログインできるメンバーに確認したところその人だけバージョン63のChromeを使っており、ログインできない組は64を使用していた。確認したところ、新型コロナウィルスの影響で延期されていたSameSite cookieの対応が7/14 バージョン84から再開されており、その影響であることが判明した。

参考

qiita.com

www.chromium.org

web.dev