CSRF 防止トークンを Cookie に入れるのが一般的なのはなぜですか? 質問する

CSRF 防止トークンを Cookie に入れるのが一般的なのはなぜですか? 質問する

私は 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 値が通常はブラウザーに保持されない異なるページ リクエスト間で状態をフローする必要がある場合に役立ちます)。

典型的なアプリケーションにおける以下のシナリオとプロセスを検討して、各アプローチの長所と短所を検討してください。これらは、シンクロナイザートークンパターン

リクエストボディアプローチ

  1. ユーザーは正常にログインしました。
  2. サーバーは認証クッキーを発行します。
  3. ユーザーはクリックしてフォームに移動します。
  4. このセッションに対してまだ生成されていない場合、サーバーは CSRF トークンを生成し、それをユーザー セッションに対して保存し、隠しフィールドに出力します。
  5. ユーザーがフォームを送信します。
  6. サーバーは、隠しフィールドがセッションに保存されたトークンと一致するかどうかを確認します。

利点:

  • 実装が簡単です。
  • AJAX で動作します。
  • フォームで動作します。
  • クッキーは実際にはHTTPのみ

デメリット:

  • すべてのフォームは HTML で隠しフィールドを出力する必要があります。
  • すべての AJAX POST にも値を含める必要があります。
  • ページは、CSRF トークンが必要であることを事前に認識して、それをページ コンテンツに含める必要があります。そのため、すべてのページのどこかにトークン値が含まれている必要があり、大規模なサイトでは実装に時間がかかる可能性があります。

カスタム HTTP ヘッダー (ダウンストリーム)

  1. ユーザーは正常にログインしました。
  2. サーバーは認証クッキーを発行します。
  3. ユーザーはクリックしてフォームに移動します。
  4. ページがブラウザに読み込まれ、CSRF トークンを取得するための AJAX リクエストが行われます。
  5. サーバーは CSRF トークンを生成し (セッション用にまだ生成されていない場合)、それをユーザー セッションに対して保存し、ヘッダーに出力します。
  6. ユーザーがフォームを送信します (トークンは隠しフィールド経由で送信されます)。
  7. サーバーは、隠しフィールドがセッションに保存されたトークンと一致するかどうかを確認します。

利点:

  • AJAX で動作します。
  • クッキーはHTTPのみ

デメリット:

  • ヘッダー値を取得するための AJAX リクエストがないと機能しません。
  • すべてのフォームでは、HTML に値が動的に追加される必要があります。
  • すべての AJAX POST にも値を含める必要があります。
  • ページは CSRF トークンを取得するために最初に AJAX リクエストを行う必要があるため、毎回追加のラウンドトリップが発生します。
  • トークンをページに出力するだけで、余分なリクエストを節約できる可能性があります。

カスタム HTTP ヘッダー (アップストリーム)

  1. ユーザーは正常にログインしました。
  2. サーバーは認証クッキーを発行します。
  3. ユーザーはクリックしてフォームに移動します。
  4. このセッションに対してまだ生成されていない場合、サーバーは CSRF トークンを生成し、それをユーザー セッションに対して保存し、ページ コンテンツのどこかに出力します。
  5. ユーザーは AJAX 経由でフォームを送信します (トークンはヘッダー経由で送信されます)。
  6. サーバーはカスタム ヘッダーがセッションに保存されたトークンと一致するかどうかを確認します。

利点:

  • AJAX で動作します。
  • クッキーはHTTPのみ

デメリット:

  • フォームでは動作しません。
  • すべての AJAX POST にはヘッダーを含める必要があります。

カスタム HTTP ヘッダー (アップストリームとダウンストリーム)

  1. ユーザーは正常にログインしました。
  2. サーバーは認証クッキーを発行します。
  3. ユーザーはクリックしてフォームに移動します。
  4. ページがブラウザに読み込まれ、CSRF トークンを取得するための AJAX リクエストが行われます。
  5. サーバーは CSRF トークンを生成し (セッション用にまだ生成されていない場合)、それをユーザー セッションに対して保存し、ヘッダーに出力します。
  6. ユーザーは AJAX 経由でフォームを送信します (トークンはヘッダー経由で送信されます)。
  7. サーバーはカスタム ヘッダーがセッションに保存されたトークンと一致するかどうかを確認します。

利点:

  • AJAX で動作します。
  • クッキーはHTTPのみ

デメリット:

  • フォームでは動作しません。
  • すべての AJAX POST にも値を含める必要があります。
  • ページは CRSF トークンを取得するために最初に AJAX リクエストを行う必要があるため、毎回追加のラウンドトリップが発生します。

Cookie の設定

  1. ユーザーは正常にログインしました。
  2. サーバーは認証クッキーを発行します。
  3. ユーザーはクリックしてフォームに移動します。
  4. サーバーは CSRF トークンを生成し、それをユーザー セッションに対して保存し、Cookie に出力します。
  5. ユーザーは AJAX または HTML フォーム経由でフォームを送信します。
  6. サーバーは、カスタム ヘッダー (または非表示のフォーム フィールド) がセッションに保存されたトークンと一致するかどうかを確認します。
  7. 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 つの一般的な理由です。

おすすめ記事