私は CSRF の問題全体と、それを防ぐ適切な方法を理解しようとしています。(私が読んで理解し、同意したリソース:OWASP CSRF 防止チートシート、CSRFに関する質問)
私の理解では、CSRF に関する脆弱性は、(Web サーバーの観点から) 受信 HTTP リクエスト内の有効なセッション クッキーが認証されたユーザーの希望を反映しているという仮定によって生じます。しかし、元のドメインのすべてのクッキーはブラウザーによってリクエストに魔法のように添付されるため、サーバーがリクエスト内の有効なセッション クッキーの存在から推測できるのは、リクエストが認証されたセッションを持つブラウザーから送信されたということだけです。そのブラウザーで実行されているコードについて、またはそれが本当にユーザーの希望を反映しているかどうかについて、それ以上推測することはできません。これを防ぐ方法は、ブラウザーの自動クッキー処理以外の手段で送信される追加の認証情報 (「CSRF トークン」) をリクエストに含めることです。大まかに言えば、セッション クッキーはユーザー/ブラウザーを認証し、CSRF トークンはブラウザーで実行されているコードを認証します。
つまり、Web アプリケーションのユーザーを認証するためにセッション クッキーを使用している場合は、各応答に CSRF トークンを追加し、各 (変化する) 要求で一致する CSRF トークンを要求する必要があります。CSRF トークンは、サーバーからブラウザーを経由してサーバーに往復し、要求を行っているページがそのサーバーによって承認されている (生成された) ことをサーバーに証明します。
私の質問は、そのラウンドトリップで CSRF トークンに使用される特定の転送方法についてです。
よくあることのようです(例えばアンギュラー、ジャンゴ、レール) を使用して、CSRF トークンを Cookie (つまり、Set-Cookie ヘッダー) としてサーバーからクライアントに送信し、クライアントの Javascript でそれを Cookie から抽出し、別の XSRF-TOKEN ヘッダーとして添付してサーバーに送り返します。
(代替方法としては、例えば急行、サーバーによって生成された CSRF トークンは、サーバー側のテンプレート拡張を介してレスポンス本文に含まれ、コード/マークアップに直接添付され、たとえば非表示のフォーム入力としてサーバーに返されます。この例は、Web 1.0 風のやり方ですが、より JS を多用するクライアントにも一般化できます。
CSRF トークンのダウンストリーム トランスポートとして Set-Cookie を使用するのがなぜ一般的なのか、またなぜこれが良いアイデアなのか。これらすべてのフレームワークの作成者は、オプションを慎重に検討し、間違えなかったのだろうと思います。しかし、一見すると、基本的に Cookie の設計上の制限を回避するために Cookie を使用するのは愚かなことのように思えます。実際、Cookie をラウンドトリップ トランスポートとして使用すると (サーバーがブラウザーに CSRF トークンを伝えるための Set-Cookie: ヘッダー ダウンストリームと、ブラウザーがサーバーにトークンを返すための Cookie: ヘッダー アップストリーム)、修正しようとしている脆弱性が再び導入されることになります。
上記のフレームワークは、CSRF トークンのラウンドトリップ全体で Cookie を使用するわけではないことはわかっています。ダウンストリームで Set-Cookie を使用し、アップストリームで別のもの (X-CSRF-Token ヘッダーなど) を使用するため、脆弱性は解消されます。ただし、ダウンストリーム トランスポートとして Set-Cookie を使用することは、誤解を招きやすく危険です。ブラウザーは、本物の悪意のある XSRF リクエストを含むすべてのリクエストに CSRF トークンを添付するようになります。これにより、せいぜいリクエストが必要以上に大きくなり、最悪の場合、善意ではあるが誤ったサーバー コードが実際にそれを使用しようとする可能性があり、これは非常に危険です。さらに、CSRF トークンの実際の受信者はクライアント側の Javascript であるため、この Cookie は http のみでは保護できません。そのため、CSRF トークンを Set-Cookie ヘッダーでダウンストリームに送信することは、私にとってはかなり最適ではないようです。
ベストアンサー1
あなたが少し触れたように、正当な理由は、CSRF クッキーが受信されると、通常のフォームと AJAX POST の両方でクライアント スクリプトのアプリケーション全体で使用できるようになることです。これは、AngularJS で使用されるような JavaScript を多用するアプリケーションでは理にかなっています (AngularJS を使用する場合、アプリケーションが単一ページ アプリである必要はありません。そのため、CSRF 値が通常はブラウザーに保持されない異なるページ リクエスト間で状態をフローする必要がある場合に役立ちます)。
典型的なアプリケーションにおける以下のシナリオとプロセスを検討して、各アプローチの長所と短所を検討してください。これらは、シンクロナイザートークンパターン。
リクエストボディアプローチ
- ユーザーは正常にログインしました。
- サーバーは認証クッキーを発行します。
- ユーザーはクリックしてフォームに移動します。
- このセッションに対してまだ生成されていない場合、サーバーは CSRF トークンを生成し、それをユーザー セッションに対して保存し、隠しフィールドに出力します。
- ユーザーがフォームを送信します。
- サーバーは、隠しフィールドがセッションに保存されたトークンと一致するかどうかを確認します。
利点:
- 実装が簡単です。
- AJAX で動作します。
- フォームで動作します。
- クッキーは実際にはHTTPのみ。
デメリット:
- すべてのフォームは HTML で隠しフィールドを出力する必要があります。
- すべての AJAX POST にも値を含める必要があります。
- ページは、CSRF トークンが必要であることを事前に認識して、それをページ コンテンツに含める必要があります。そのため、すべてのページのどこかにトークン値が含まれている必要があり、大規模なサイトでは実装に時間がかかる可能性があります。
カスタム HTTP ヘッダー (ダウンストリーム)
- ユーザーは正常にログインしました。
- サーバーは認証クッキーを発行します。
- ユーザーはクリックしてフォームに移動します。
- ページがブラウザに読み込まれ、CSRF トークンを取得するための AJAX リクエストが行われます。
- サーバーは CSRF トークンを生成し (セッション用にまだ生成されていない場合)、それをユーザー セッションに対して保存し、ヘッダーに出力します。
- ユーザーがフォームを送信します (トークンは隠しフィールド経由で送信されます)。
- サーバーは、隠しフィールドがセッションに保存されたトークンと一致するかどうかを確認します。
利点:
- AJAX で動作します。
- クッキーはHTTPのみ。
デメリット:
- ヘッダー値を取得するための AJAX リクエストがないと機能しません。
- すべてのフォームでは、HTML に値が動的に追加される必要があります。
- すべての AJAX POST にも値を含める必要があります。
- ページは CSRF トークンを取得するために最初に AJAX リクエストを行う必要があるため、毎回追加のラウンドトリップが発生します。
- トークンをページに出力するだけで、余分なリクエストを節約できる可能性があります。
カスタム HTTP ヘッダー (アップストリーム)
- ユーザーは正常にログインしました。
- サーバーは認証クッキーを発行します。
- ユーザーはクリックしてフォームに移動します。
- このセッションに対してまだ生成されていない場合、サーバーは CSRF トークンを生成し、それをユーザー セッションに対して保存し、ページ コンテンツのどこかに出力します。
- ユーザーは AJAX 経由でフォームを送信します (トークンはヘッダー経由で送信されます)。
- サーバーはカスタム ヘッダーがセッションに保存されたトークンと一致するかどうかを確認します。
利点:
- AJAX で動作します。
- クッキーはHTTPのみ。
デメリット:
- フォームでは動作しません。
- すべての AJAX POST にはヘッダーを含める必要があります。
カスタム HTTP ヘッダー (アップストリームとダウンストリーム)
- ユーザーは正常にログインしました。
- サーバーは認証クッキーを発行します。
- ユーザーはクリックしてフォームに移動します。
- ページがブラウザに読み込まれ、CSRF トークンを取得するための AJAX リクエストが行われます。
- サーバーは CSRF トークンを生成し (セッション用にまだ生成されていない場合)、それをユーザー セッションに対して保存し、ヘッダーに出力します。
- ユーザーは AJAX 経由でフォームを送信します (トークンはヘッダー経由で送信されます)。
- サーバーはカスタム ヘッダーがセッションに保存されたトークンと一致するかどうかを確認します。
利点:
- AJAX で動作します。
- クッキーはHTTPのみ。
デメリット:
- フォームでは動作しません。
- すべての AJAX POST にも値を含める必要があります。
- ページは CRSF トークンを取得するために最初に AJAX リクエストを行う必要があるため、毎回追加のラウンドトリップが発生します。
Cookie の設定
- ユーザーは正常にログインしました。
- サーバーは認証クッキーを発行します。
- ユーザーはクリックしてフォームに移動します。
- サーバーは CSRF トークンを生成し、それをユーザー セッションに対して保存し、Cookie に出力します。
- ユーザーは AJAX または HTML フォーム経由でフォームを送信します。
- サーバーは、カスタム ヘッダー (または非表示のフォーム フィールド) がセッションに保存されたトークンと一致するかどうかを確認します。
- Cookie は、CSRF トークンを取得するためにサーバーに追加のリクエストを送信することなく、追加の AJAX およびフォーム リクエストで使用するためにブラウザーで使用できます。
利点:
- 実装が簡単です。
- AJAX で動作します。
- フォームで動作します。
- クッキー値を取得するために必ずしも AJAX リクエストは必要ありません。任意の HTTP リクエストでクッキー値を取得でき、JavaScript 経由ですべてのフォーム/AJAX リクエストに追加できます。
- CSRF トークンが取得されると、その値は Cookie に保存されるため、追加のリクエストなしで再利用できます。
デメリット:
- すべてのフォームでは、HTML に値が動的に追加される必要があります。
- すべての AJAX POST にも値を含める必要があります。
- クッキーはリクエストごとに送信され(つまり、CSRF プロセスに関係のない画像、CSS、JS などのすべての GET)、リクエスト サイズが増加します。
- クッキーはHTTPのみ。
そのため、クッキーアプローチはかなり動的であり、クッキー値(任意のHTTPリクエスト)を取得して使用する簡単な方法を提供します(JSは任意のフォームに値を自動的に追加でき、ヘッダーまたはフォーム値としてAJAXリクエストで使用できます)。セッションのCSRFトークンを受け取ったら、CSRFエクスプロイトを使用する攻撃者はこのトークンを取得する方法がないため、再生成する必要はありません。悪意のあるユーザーが上記のいずれかの方法でユーザーのCSRFトークンを読み取ろうとした場合、これは次の方法で防止されます。同一起源ポリシー悪意のあるユーザーがサーバー側で CSRF トークンを取得しようとすると ( などcurl
)、被害者の認証セッション クッキーがリクエストから失われるため、このトークンは同じユーザー アカウントに関連付けられません (これは攻撃者のものになるため、サーバー側で被害者のセッションに関連付けられません)。
また、シンクロナイザートークンパターンまた、ダブル送信クッキーCSRF 防止方法では、当然ながら Cookie を使用して CSRF トークンのタイプを保存します。CSRF トークンのサーバー側状態を必要としないため、実装が簡単です。この方法を使用する場合、CSRF トークンは実際には標準の認証 Cookie になる可能性があり、この値は通常どおりリクエストとともに Cookie 経由で送信されますが、値は隠しフィールドまたはヘッダーのいずれかでも繰り返されます。攻撃者はそもそも値を読み取ることができないため、これを複製することはできません。ただし、認証 Cookie 以外の別の Cookie を選択することをお勧めします。これにより、認証 Cookie は HttpOnly としてマークされて保護されます。これが、Cookie ベースの方法を使用して CSRF を防止するもう 1 つの一般的な理由です。