WebAPI
csrf

このWeb APIってCSRF対策出来てますか?って質問にこたえよう

前文

時々、このWeb APIってCSRF対策出来てますか?とか
そのCSRF対策ってなんで安全なんですか?とか、そういう質問を友人・知人・同僚から受けます。

その質問に対して、都度回答をしているのですが、改めて記事としてまとめようかな、というのがこの記事です。
もし私の認識に穴・誤りがありましたら、ぜひ指摘お願いします。

前提事項

この記事中では、CSRF対策のみにフォーカスします。
そのため、以下はスコープ外です(無駄な議論回避のための前提事項です)。
セキュリティに銀の弾丸などないので、複数の脆弱性対策を組み合わせて、安全に行きましょう。

  • XSS脆弱性により、CSRFが可能である
  • ブラウザの脆弱性により、CSRFが可能である
  • XXXの脆弱性により、CSRFが可能である

前提知識

CSRF対策について語る上で必要な知識はいくつかありますが、この記事では以下の知識を前提に扱います。
ご存知でしたら、読み飛ばしてください!

1. リクエスト強要(CSRF:Cross-site Request Forgery)

対策を理解するには、まずCSRF攻撃の知識は当然必要ですよね!
この記事の理解には、最低限以下のトピックの理解が必要です!

また、実際のCSRF脆弱性を利用された事例として、mixiのはまちちゃん事件も。
大量の「はまちちゃん」を生み出したCSRFの脆弱性とは? - ITmedia エンタープライズ

2. 同一生成元ポリシー(Same-Origin Policy)とクロスオリジンHTTPリクエスト(CORS)

CSRF攻撃の知識については、もう十分?
じゃあ、今度は対策を考える上で必要な知識です。

攻撃者の邪悪なサイト https://evil.example.com を、被害者が踏んだ時に、どういうリクエストが飛んでくるのか。
以下の2資料が非常にわかりやすいので、こちらを前提知識とします。

CSRF対策ってCSRFトークン使っとけばいいんでしょ?という誤解

CSRF対策といえばCSRFトークンでしょ?CSRFトークン使っとけば安全なんでしょ?というのは、必ずしも正しくありません。
CSRFトークンの作り方次第では脆弱になりえます。
アプリケーション開発者としては、なんでこのCSRFトークンがCSRF対策になっているのか?を答えられるようになっておきたいですね。

CSRF対策の基本は、リクエストの文脈を検証すること

リクエストの文脈とは、以下のようなものです。

  • このユーザーIDが
  • この時間までに
  • このページ,このサービス,このアプリケーションから
  • このようなリクエストをする

例えば、このユーザーIDがという文脈を検証できなければ、何が起こるでしょうか?
攻撃者が予め100000000個ぐらいのCSRFトークンを攻撃者のユーザーIDで作っておけば、それをたくさんの被害者に使わせて攻撃出来ちゃいますよね。

CSRF対策の基本のキ

長々と書いてしまいましたが、CSRF対策の基本は以下の2フェーズで処理を行うことです。
逆にいうと、この2フェーズで処理さえ出来ていれば、CSRFトークンも不要です。

  1. 文脈の正当性チェック
  2. 目的のリクエスト処理

CSRFトークンによるCSRF対策

image.png

CSRFトークンによる対策は、最もメジャーな対策方法です。
メリットは、使い古された対策であること、特に様々なところで解説されており、
開発者が理解しやすいこともあげられると思います。

デメリットは、クライアント開発者はリクエストする際にCSRFトークンの取得を実装しなければいけない点。
ほんの少しのことだけれど、意外と面倒くさい。

CSRFトークン方式による対策についてのよくある質問

Q.攻撃者サイト( https://evil.example.com )で、トークンAPIから目的のAPIまでの一連の流れをそのままやれば、攻撃出来ちゃうんじゃない?

A.とても良い質問ですね、しかし大丈夫です。攻撃できません。
前提知識にもあったSame Origin Policyによって、攻撃者はトークンを取得することが出来ません。
正確には、トークンAPIへのリクエストは成功し、トークンは払い出されますが、ブラウザがレスポンスを読み取らせてくれません。

Q.トークンAPIが無防備じゃないですか?トークンは払い出されてもいいんですか?

A.実装によりますが、大体大丈夫です。
まずセキュリティ上の問題はないでしょう。最初の質問にあるように、Same Origin Policyによって防がれます。
次にサービス運用上の問題ですが、CSRFトークンに期限があれば、大量のトークンを嫌がらせのように作られたとしても
期限を迎えたものから削除すればよいため、サービス影響は非常に少ないでしょう。

期限がなくサーバー側にトークンを無限に保存するような場合は、以下のような対策が必要そうですね。

  • 十分なスケーラビリティのあるデータストアに保存する
  • 大量のリクエストを制限するような機構を入れる

また、サーバー側にCSRFトークンを保存しない、ということもJSON Web Tokenを使うとできます。
検証もCPUコストだけで済むので、マイクロサービス化している場合、とても便利ですね。
JSON Web Token の効用 - Qiita

カスタムヘッダ(X-Requested-WithやX-Fromなど)によるCSRF対策

image.png

以下のページなどで提案・検証されているカスタムヘッダを利用したCSRF対策です。
独自ヘッダをチェックするだけのステートレスなCSRF対策は有効なのか? — A Day in Serenity (Reloaded) — PHP, FuelPHP, Linux or something

発想の源は以下です。

  • 他オリジンからのリクエスト時には、ブラウザが勝手にpreflightリクエストを投げてくる
  • このpreflightリクエスト内で文脈を検証が出来れば、ステートレスな(CSRFトークン不要な)CSRF対策が出来るのでは?

これを達成するためには、必ずpreflightリクエストを投げるようにする必要があり、その条件を満たすために、カスタムヘッダが利用されます。
参考:ブラウザがpreflightリクエストを投げる条件

メリットは、CSRFトークンを利用するケースと異なり、クライアント側はカスタムヘッダを付与するようにしておくだけで済むということ。

デメリットは、カスタムヘッダを付けるために、HTMLの<form>などを使ったリクエストは出来ず、ブラウザからはjavascriptが必須になること。

まとめ

  • CSRF対策は、文脈の正当性チェックによって実現される
  • 正当性チェックの方法は、CSRFトークンだけではない