■ 1. CSRF攻撃の成立条件
- CSRF(Cross-Site Request Forgery)は古くから知られる攻撃手法である
- 攻撃者が用意したサイトに仕込まれたフォームから、ユーザがログイン中のサービスに対してリクエストを送信する攻撃である
- ログイン済みのCookieが付与されたリクエストがサービスの投稿APIに準拠していれば、そのユーザのアカウントで不正な投稿が受理される
- 攻撃の手軽さと影響の大きさによって、XSSやSQLインジェクションと並ぶ有名な攻撃手法として認知された
- 従来の対策としては、One Time Token(CSRFトークン)をフォームに仕込み、トークンの一致によって投稿を受理する方法が一般的だった
■ 2. CSRF成立の本質的問題点
- CSRF攻撃が成立する根本原因は「攻撃者のフォームからのリクエストにもサービスのCookieが付与される」点である
- しかし、CSRFトークンが機能していた事実は「このリクエストはどこから来たものなのか」が分かれば対策できる証拠である
- 本来注目すべき欠落は「リクエストの出自がわからない」という点である
- SameSite Cookieの導入によって別のサイトからのリクエストにCookieが付与されないようにすることは対策として成立するが、本質的な問題はリクエストの出自が不明であることである
■ 3. Originヘッダの付与
- プラットフォームの回答は「リクエストにOriginヘッダを付与する」というものである
- 現在のブラウザでフォームをsubmitすると、送られるリクエストにOriginヘッダが付与される
- Originヘッダの値を確認すれば、リクエストが正規のサイトからではないことを容易に判別できる
- Cookieの有無に関わらず、サービスは「意図しないOriginからのリクエスト」を弾くことができる
- このヘッダの必要性は少なくとも16年前から議論されており、7年前にFirefoxが実装することで全てのブラウザがフォームのsubmitにOriginヘッダを付与するようになった
- SameSite Cookieが導入されるずっと以前から「リクエストの出自を知る」ことは可能であり、それを用いて攻撃リクエストを弾くことができた
- fetch()を用いた実装でOriginを確認しながら適切なAccess-Control-Allow-Originを返していれば、「どこから来たかわからないリクエスト」を弾くことはずっと可能であった
■ 4. SameSite Cookieの副次的効果
- SameSite Cookieの登場により、積極的な対策をしてこなかったサービスも受動的な変更によって保護される結果になった
- しかしこれはかなり副次的な効果である
- SameSite Cookieの本来の目的は3rd Party Cookieをマークすることであり、3rd Party Cookie Deprecateの終着点はSameSite=None Cookieが送られないようにすることである
- CSRFは3rd Party Cookieよりも遥かに古くから問題だったが、「サイトを跨いだCookieが送られること」よりも「リクエストの出自がわからないこと」の方がプラットフォームが対策すべき問題とされていた
- SameSite Laxがデフォルトになったことを理由に「Originのチェック」を怠った実装は、本質的な対策を怠った片手落ちの実装である
■ 5. CSRFトークンの必要性の再検討
- OWASPのCSRF Cheat Sheetでは今でもトークンベースの対策が推奨されている
- トークンベースの対策を推奨する理由として以下が挙げられる:
- XSSがあった場合の対策
- ヘッダを改変している可能性
- GETでAPIがあると成立する攻撃
- サブドメインが乗っ取られる攻撃
- XSS問題の反論:
- XSS自体を対策すべきであり、XSSによってDOMに展開されたCSRFトークンを盗めない道理はない
- ヘッダ改変問題の反論:
- ブラウザや拡張に脆弱性があれば、サービス側の対策はバイパスできるため、サービス提供者が想定すべき対策として視点がずれている
- ユーザ設定やProxyが意図的にOriginヘッダを改変する環境は、ルールを守っていないためサービス側が許容する必要はない
- GET API問題の反論:
- 様々な条件でSameSiteやOriginが機能しないリクエストを生成できるという指摘は、「APIがGETだった場合に攻撃が成立する」という条件に収束する
- 副作用のあるAPIをGETで提供している実装は前提として間違っている
- サブドメイン攻撃の反論:
- SameSite Cookieだけに依存した対策は問題があるため、一次防御は「リクエストのOriginのチェック」である必要がある
- CSRFトークンの利点:
- 実装がこなれており堅牢である
- フレームワークがデフォルトで提供することが多く、導入コストが低い
- 現状入っているなら積極的に外す理由はない
- 防御の認識の変更:
- CSRFトークンは一層目の防御ではなく、多層防御の二層目であると認識すべきである
- トークンを使用していても「リクエストの出自を確認する」実装は一層目にあるべきである
- 全ての場所でOriginを確認するのがプラクティスである
■ 6. Fetch Metadataの導入
- 本来は全てのリクエストにOriginをつけるべきだが、「OriginヘッダのあるリクエストはXHRからのもの」という前提の実装が蔓延していたため、ドラスティックな変更ができなかった
- Originヘッダとは別にFetch Metadataが定義された
- 現在のブラウザでは以下のヘッダが付与される:
- Sec-Fetch-Dest
- Sec-Fetch-Mode
- Sec-Fetch-Site
- Sec-Fetch-User
- リクエストの出自を確認する手法は整備されており、これらを無視することはプラットフォームが差し伸べている手を振り払うことと同じである
■ 7. 令和時代の実装プラクティス
- 副作用があるエンドポイントの実装における優先順位:
- POSTにする(副作用のあるAPIをGETにしない)
- Originを確認する
- SameSite Lax/Strictを明示する
- Fetch Metadataも確認する
- Fetch Metadataのサポートに不安がある場合は「存在したら値をチェックする」実装でも良い
- Sec-プレフィックスはJavaScriptから操作できないヘッダであるため、値がある場合だけのチェックでも意味がある
- 実装の特徴:
- 追加のストレージコストが不要である
- コードだけで実装できるためレイテンシーが最小である
- フレームワークのレールの基盤として存在することが望ましい
- この実装から逸脱するコードが必要になった場合、プラットフォームが推奨するレールから外れていることを認識した上で、CORSなどの適切な対策をしながら拡張すべきである
- このベースを伴わずにトークンを載せても片手落ちである
- この実装をベースにしてもトークンがないと防げないCSRFが可能であれば、それはプラットフォームにおけるバグの可能性が高く、W3CのWebAppSecなどで議論すべき題材である