OWASP ZAP のCSRF対策トークン自動生成処理は何をしているのか?
※当サイトにはプロモーションが含まれています。
公開日:
更新日:
OWASP Zed Attack Proxy (ZAP)とは?でも紹介しているように、OWASP ZAPにはCSRF対策トークンを自動で生成してくれる機能があります。これは、自動検査中にパラメータとしてCSRF対策トークンが必要なURLにアクセスした場合、そのトークンを自動で生成して、その値をパラメータとしてセットしてくれる機能です。Webアプリケーションの実装によってはCSRF対策トークンに毎回違う値が用意される場合があるため、この機能があるとそのような場合でも正しくリクエストを送信することができるようになります。
これはどうやっているのでしょうか? ソースコードを追って何をやっているのかを説明します。
OWASP ZAP v2.2.2
主な関連パッケージ
処理段階1 CSRF対策トークンの情報収集
手動アクセス or スパイダリング時
- レスポンス受信時に scanHttpResponseReceive が呼ばれ、その中でリクエスト内にCSRF対策トークンが含まれているか確認します。(CSRF対策トークンの名前はオプションで予め登録されており、追加することもできます)
- もし見つかれば、トークン名・トークン値・トークンが含まれていた時にアクセスしたURLがExtensionAntiCSRF のインスタンス変数に登録されます。
セッション変更時
- セッションを変更した場合、切り替えた後のセッションでそれまで手動アクセスしてきたデータ(内部で保存されています)のレスポンスボディ内をチェックして、CSRF対策トークンの値を取得(あれば)し直します。(sessionChanged)。
- 取得したトークン名・トークン値・そのトークンが含まれていたレスポンスボディを取得したURLはExtensionAntiCSRF のインスタンス変数に登録されます。
処理段階2 自動診断処理中(Active Scan or fuzzer)の毎回のリクエスト送信時
1. リクエストボディの中にCSRF対策トークンのフィールドがあるかチェック
- ActiveScanの場合、リクエスト生成時(送信前)に各ルール(非破壊的な攻撃を表す各クラス)の継承元である以下のメソッドが呼ばれます。
- org.parosproxy.paros.core.scanner.AbstractPlugin.sendAndReceive(…)
- この中の以下の部分で、以前取得したCSRF対策トークンの値がリクエストボディに含まれているか確認します。
- List
tokens = extAntiCSRF.getTokens(msg);
- Fuzzの場合は、以下のソースコードの中でActiveScanと同じように、リクエスト中にCSRF対策トークン値が含まれているか確認します。
- 共通して言えること
- リクエストボディ(POSTパラメータ)内にCSRF対策トークンがあるか探す処理では、トークン名ではなく、以前のアクセス時に生成されたCSRF対策トークンの値があるかどうかで判断します。
- org.zaproxy.zap.extension.anticsrf.ExtensionAntiCSRF.getTokens(String, String)
2. リクエストボディにCSRF対策トークンの値が含まれていた場合の処理
- 見付かったCSRF対策トークンを生成したURLにアクセスし、トークンの値を生成させて取得します。
- リクエストボディ内で見付かったCSRF対策トークン値は、以前それをレスポンスボディから取得した時のURLが紐付いています。
- リクエストボディ内の古い値の部分を新しく生成した値に置換してリクエストを送信します。
- 以上です。
アンチCSRFトークン機能を利用するための設定
- [設定] - [アンチ CSRF トークン] で、検査対象サイトで使われている アンチCSRFトークン名を登録する。
- [設定] - [動的スキャン] で、[アンチCSRFトークン利用] にチェックを入れる。
その他のメモ
- この機能はスキャナーがシングルスレッドで動くように強制されるので、パフォーマンスがかなり悪くなるそうです。
- 参照
- これからリクエストを送ろうとしているURLがCSRF対策トークンを含む場合に、CSRF対策トークンを再生成するわけですが「CSRF対策トークンを再生成するリクエストの送信」と「再生成したCSRF対策トークンをセットしたリクエストの送信」は続けて送信される必要があります。というのは、この間のタイミングで別スレッドによってCSRF対策トークンが再生成されてしまうと、最初に再生成した値は無効になってリクエストがWebサーバから拒否されてしまう可能性があるからです。あくまで予想ですが、このあたりがマルチスレッドにできない原因なのではないかと思います。ただこの機能はまだテスト実装のようなので、今後改善されるのではないでしょうか。
