Railsを使用していると、普段何気なく運用しているCSRFという仕組み。
このCSRFについて、Railsのバージョンアップ時に苦戦した経緯がありましたのでシェアしたいと思います。
そもそもCSRFとは?という方については、下記に分かりやすく解説してくれている記事がありますのでそちらを参考にして頂ければと思います。
RailsのCSRF対策について
Railsのバージョンによって生成されるCSRFトークンが異なる
今回のバージョンアップではRailsを6.1→7.0に上げたのですが、この旧バージョンと新バージョンで生成されるCSRFトークンが異なります。どのような違いかというと、旧バージョンでは通常のBase64でトークンがエンコードされるのに対し、新バージョンではURLセーフなBase64でエンコードするようになりました。
そしてこのトークンが異なる状態で、ページ遷移するとArgumentErrorを引き起こします。(この時は旧バージョンから新バージョン、新バージョンから旧バージョンどちらのケースもエラーが起きると思っていました...)
通常のBase64とURLセーフなBase64との違いについてはこちら
正確にはこの新方式でエンコードされるようになったのは、Railsの6.1からでしたが、以前のバージョンアップ時の6.0から6.1にする際に下記の設定を入れていたことによって、現行でも旧バージョンでエンコードするように動いていました。
Rails.application.config.action_controller.urlsafe_csrf_tokens = false
こちらのコードはnew_framework_defaults_6.1.rbに記載されており、ここでfalseを設定しておくことで6.1のバージョンであっても旧方式でエンコードされるようになります。falseに設定されていたのは今回直面している問題点と一緒で以前に6.0から6.1にする際に生成されるエンコードが異なることでエラーが生じることを防ぐ為にここにその設定を入れていたのでした。(正確には新バージョンから旧バージョンへのアクセス対策)
今回のバージョンアップに際し、この設定を残しておくことでエラーを回避することはできますが、Rails7.0以降はこちらの設定オプションは非推奨になっていくということで、ここの設定を失くす為に対策が必要になりました。
対策案(トークンが異なった場合、保存されている値をリセットして新たなトークンをセットし直す)
対策案の一つとして、保存されているトークンが異なった場合、保存されているトークンをリセットして新たなトークンを設定するというもの。headタグ内に記載している(csrf_meta_tags)を(rescued_csrf_meta_tags)として書き換えてやり、下記のロジックが呼ばれるようにします。
def rescued_csrf_meta_tags
csrf_meta_tags
rescue ArgumentError
request.reset_session
csrf_meta_tags
end
トークンが異なっていた場合、ArgumentErrorが発生するのでそれを拾ってやり、新たなトークンをセッションに保存しています。
この形で通常のページ遷移であれば対応できることが確認できました。しかし、例えば送信ボタンなどを押したポスト処理を行う場合、別途用意されているauthenticity_tokenを参照する仕組みになっているので、今回のロジックで拾ってやることができません。
どうしようかと悩んで調査をしていくと、ある事実が分かりました。
新バージョンで生成されたトークンで旧バージョンにPOSTするとエラーがおきますが、旧バージョンから新バージョンにPOSTした場合には問題なく動作できるということ。(これは生成されるトークンの互換性による関係ということで、もっと早く気づくべきでした...)
最終対策
上記の調査結果により、通常通りのアップデートに関しては問題ないことが分かりましたが、例えば新バージョンリリース後に別の問題が発生して、急遽旧バージョンに戻したいといった事象が発生した場合エラーが出ることに変わりないので、先程のfalseの設定を残してやることにしました。
こちらの設定を残してやることにより、旧バージョンから新バージョン・新バージョンから旧バージョンどちらの場合においてもエラーを発生させることなく対応できます。そして、新バージョンをリリースして1・2週間様子を見て問題なければfalse設定を外すことで無事に移行が完了します。
終わりに
旧バージョンで生成されたトークンは新バージョンでも互換性があることにもっと早く気づけていたら、そこまで悩むこともなかったかなという感じでした。ただCSRFの仕組みについては、普段何気なく使用しているだけでその実態が何なのかということが今回の調査で理解できたので、その点は良かったのかなと。バージョンアップに際して、この他にも問題があったので皆さんもバージョンアップ時には十分気をつけてください。