タイトル通りです。CSRFトークンはもはや旧ブラウザへの対応が必要である場合を除いて不要です。CSRFトークンを頑張って実装しなくても、CookieのSameSite
属性を用いればCSRFは防御可能です。
CSRFの原因
そもそもCSRFはあるオリジンから別のオリジンへCookie付きのPOSTリクエストを行えてしまうことによって起きる問題でした。たとえばCSRFの脆弱性があるbank.example.com
という銀行サイトにログインした状態で、下記のコードが含まれる第三者の悪意あるサイトmalicious.example.com
を開いて「今すぐクリック!!!」をクリックすると、お金が勝手に送金されてしまいます。
<form action="https://bank.example.com/transfer" method="post">
<input type="hidden" name="to" value="cracker" />
<input type="hidden" name="amount" value="¥10000000" />
<button>今すぐクリック!!!</button>
</form>
問題は何の関係性もないmalicious.example.com
のサイトから発せられたbank.example.com
へのリクエストにbank.example.com
の認証情報(Cookie)が付与されてしまうことです。これによってbank.example.com
に対するユーザ認証付きの操作を第三者がそれと分からない形で実行させることができてしまうわけです。
そこで導入されたのがSet-Cookie
ヘッダのSameSite
属性です。
SameSite
属性
SameSite
はSet-Cookie
ヘッダの属性でたとえば以下のように設定します。
Set-Cookie: sessionId=9284769; SameSite=Lax
SameSite
属性の値は3種類、None
、Lax
、Strict
です。
Lax
またはStrict
を指定すればCSRFは起こりません。Strict
を指定するとブラウザは別オリジンのサイトによって開始されたリクエストではCookieを送信しません。None
は昔のブラウザと同じ挙動で、常にCookieを送信します。Lax
はGETリクエストの場合はNone
相当だが、POSTリクエストの場合はStrict
相当になるといった設定です。Lax
もしくはStrict
であれば別の第三者のサイトからのPOSTリクエスト時にはCookieを送信しないのでCSRFは起こり得ません。
上の例のフォームではmalicious.example.com
上のサイトから開始されたhttps://bank.example.com/transfer
へのリクエストは別オリジンへのリクエストとなるためCookieが付与されず、銀行サイトのバックエンドからはユーザ認証がないリクエストとして扱われます。そうなれば当然送金はできずに攻撃は失敗するというわけです。
多くの場合、Strict
は厳しすぎるのでLax
を使うことになると思います。Strict
を指定した場合、GETメソッドでもCookieが送られないと言うことは、別のサイトに自分のサイトのリンクが貼ってあったとき、それをクリックしてもCookieが送信されない、つまり必ずログインされていない状態で遷移するということです。一般公開しているサイトならそれは不便すぎますよね。
ブラウザサポート状況
SameSite
属性はすでにモダンブラウザの全てで、最低でも2,3年前からサポートされています。
Google ChromeではSameSite属性を指定しない場合のデフォルト値がLax
になっています。しかしFirefoxやSafariは互換性重視でデフォルトがNone
なので指定は必須です。
SafariやIE11の数年前のバージョンとマイナーなモバイルブラウザではサポートされないものもあるようです(2023年1月現在)。彼らのためにCSRFトークンを実装してあげるかどうかと言う話ですね。私としては、ここまで普及していて特に難易度が高いとも思えないセキュリティ機能を未だに実装していないようなブラウザはそもそも使うべきではない、と言う感想ですが。
まとめ
- CSRF対策は
Set-Cookie
ヘッダにSameSite=Lax
を付与すれば足りる。 -
SameSite
属性はモダンブラウザの全てで数年前からサポート済み。 - CSRFトークンは古かったり一部のマイナーなブラウザへの対応が必要な場合を除いて不要。