[解決済み] なぜCSRF防止トークンをクッキーに入れるのが一般的なのですか?
質問
CSRFの全体像と、それを防ぐ適切な方法を理解しようとしています。(私が読み、理解し、同意しているリソース。 OWASP CSRF Prevention CHeat Sheet(CSRF防止シート , CSRFに関する質問 .)
私の理解では、CSRFに関する脆弱性は、(ウェブサーバから見て)受信するHTTPリクエストの有効なセッションクッキーが認証されたユーザの希望を反映していると仮定することによってもたらされます。しかし、オリジンドメインのすべてのクッキーは、ブラウザによって魔法のようにリクエストに添付されるので、実際にサーバがリクエストの有効なセッションクッキーの存在から推測できることは、リクエストが認証されたセッションを持つブラウザから来たということだけです。 コード そのブラウザで実行されていることが、本当にユーザーの希望を反映しているかどうか。これを防ぐには、ブラウザの自動クッキー処理以外の方法で、追加の認証情報("CSRFトークン")をリクエストに含める必要があります。ざっくり言うと、セッションクッキーはユーザー/ブラウザーを認証し、CSRF トークンはブラウザーで実行されるコードを認証します。
つまり、セッションクッキーを使ってウェブアプリケーションのユーザーを認証する場合、各レスポンスにCSRFトークンを追加し、各(変異する)リクエストに一致するCSRFトークンを要求する必要があるということです。CSRF トークンは、サーバからブラウザへ、そしてサーバへと往復し、リクエストを行うページがそのサーバによって承認された(あるいは生成された)ものであることをサーバに証明するのです。
CSRFトークンの往復に使用される特定のトランスポートメソッドについての質問です。
一般的なようです(例えば アンギュラーJS , Django , Rails ) を使って、CSRF トークンをサーバーからクライアントへクッキーとして送信し (つまり Set-Cookie ヘッダーで)、クライアント内の Javascript にクッキーからそれを取り出して、別の XSRF-TOKEN ヘッダーとしてサーバーに送り返すようにします。
(別の方法として、例えば、以下のような方法が推奨されています。 エクスプレス この場合、サーバによって生成された CSRF トークンは、サーバサイドのテンプレート展開によってレスポンスボディに含まれ、サーバに返すコードやマークアップに直接添付されます (例: hidden フォーム入力)。この例は、よりWeb 1.0的なやり方ですが、JSを多用するクライアントにも問題なく適用できます)。
CSRFトークンの下流トランスポートとしてSet-Cookieを使用することが一般的な理由/これはなぜ良いアイデアなのでしょうか?これらのフレームワークの作者は、選択肢を慎重に検討し、これを間違っていなかったと想像しています。しかし、一見したところ、本質的にクッキーの設計上の制限を回避するためにクッキーを使用することは、愚かなことのように思えます。実際、クッキーをラウンドトリップトランスポートとして使用した場合(CSRFトークンをブラウザに伝えるためにサーバーが下流のSet-Cookie:ヘッダー、それをサーバーに返すためにブラウザが上流のCookie:ヘッダー)、修正しようとしている脆弱性を再び誘発することになります。
彼らは下流でSet-Cookieを使い、上流で別のもの(例えばX-CSRF-Tokenヘッダー)を使い、そしてこれは脆弱性を閉じます。ブラウザは、本物の悪意のあるXSRFリクエストを含むすべてのリクエストにCSRFトークンを添付することになります。さらに、CSRF トークンの実際の受信者はクライアントサイドの Javascript であるため、このクッキーは http-only では保護できないことになります。CSRFトークンをSet-Cookieヘッダで下流に送ることは、私にはかなり不適切に思えます。
解決するには?
CSRFクッキーを受け取ると、通常のフォームとAJAX POSTの両方で、クライアントスクリプトでアプリケーション全体に使用できるようになるからです。これは、AngularJSで採用されているようなJavaScriptの重いアプリケーションで意味を持ちます(AngularJSの使用は、アプリケーションが単一ページであることを必要としないので、CSRF値が通常ブラウザで持続できない異なるページリクエスト間で状態を流す必要がある場合に有用です)。
あなたが説明した各アプローチのいくつかの長所と短所について、典型的なアプリケーションの次のシナリオとプロセスを考えてみてください。これらは シンクロナイザー・トークンパターン .
リクエストボディの考え方
- ユーザーがログインに成功しました。
- サーバーが認証クッキーを発行する。
- ユーザーがクリックし、フォームに移動する。
- このセッションでまだ生成されていない場合、サーバーはCSRFトークンを生成し、それをユーザーセッションに対して保存し、hiddenフィールドに出力します。
- ユーザーがフォームを送信します。
- サーバーが hidden フィールドがセッション保存されたトークンと一致することを確認します。
メリット
- 実装が簡単である。
- AJAXで動作します。
- フォームを使用します。
- クッキーは、実際には HTTPのみ .
デメリット
- すべてのフォームで、HiddenフィールドをHTMLで出力する必要があります。
- AJAX POSTも値を含める必要があります。
- CSRF トークンを必要とすることをページが事前に知っていて、ページのコンテンツに含めることができるため、すべてのページがトークンの値をどこかに含まなければなりません。
カスタムHTTPヘッダー(下流)
- ユーザーがログインに成功しました。
- サーバーが認証クッキーを発行する。
- ユーザーがクリックし、フォームに移動する。
- ブラウザにページが読み込まれ、CSRF トークンを取得するために AJAX リクエストが実行されます。
- サーバーはCSRFトークンを生成し(まだセッション用に生成されていない場合)、ユーザーセッションに対して保存し、そのトークンを ヘッダを作成します。
- ユーザーがフォームを送信する(トークンはhiddenフィールドを介して送信される)。
- サーバーがhiddenフィールドがセッション保存されたトークンと一致することを確認する。
メリット
- 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 リクエストを行う必要があります。
設定-クッキー
- ユーザーがログインに成功しました。
- サーバーが認証クッキーを発行する。
- ユーザーがクリックし、フォームに移動する。
- サーバーはCSRFトークンを生成し、ユーザーセッションに対して保存し、クッキーに出力します。
- ユーザーがAJAXまたはHTMLフォームでフォームを送信します。
- サーバーがカスタムヘッダ(または隠しフォームフィールド)がセッションで保存されたトークンと一致するかどうかを確認します。
- CSRF トークンを取得するためにサーバーに追加のリクエストをすることなく、追加の AJAX やフォームのリクエストで使用できるようにブラウザでクッキーを使用できます。
メリット
- 実装が簡単である。
- AJAXで動作します。
- フォームを使用します。
- クッキーの値を取得するために、必ずしもAJAXリクエストが必要なわけではありません。どのようなHTTPリクエストでもそれを取得することができ、JavaScriptを介してすべてのフォーム/AJAXリクエストに追加することができます。
- CSRFトークンを一度取得すると、その値はクッキーに保存されるため、追加のリクエストなしに再利用することができます。
デメリット
- すべてのフォームのHTMLに動的に値を追加する必要があります。
- AJAX POSTも値を含める必要があります。
- クッキーの送信は あらゆる リクエスト(つまり、CSRF処理に関与しない画像、CSS、JSなどのすべてのGET)のサイズが大きくなります。
- クッキーは HTTPのみ .
つまり、クッキーのアプローチはかなり動的で、クッキーの値を取得する簡単な方法(あらゆるHTTPリクエスト)とそれを使用する方法(JSはあらゆるフォームに自動的に値を追加でき、AJAXリクエストではヘッダーまたはフォーム値として使用できる)を提供します。CSRF トークンをいったんセッションで受け取ると、CSRF を悪用する攻撃者はこのトークンを取得する方法がないため、トークンを再生成する必要 はありません。悪意のあるユーザが上記のいずれかの方法でユーザの CSRF トークンを読み取ろうとした場合、CSRF トークンを読み取れないようにするために
同一生成元ポリシー
. 悪意のあるユーザが CSRF トークンをサーバ側で取得しようとした場合 (例.
curl
というのも、被害者の認証セッションクッキーがリクエストに含まれていないからです (これは攻撃者のもので、被害者のセッションとサーバ側で関連付けられることはありません)。
と同様に シンクロナイザートークンパターン があります。 ダブルサブミットクッキー CSRFの防止方法は、もちろんCSRFトークンの一種を保存するためにクッキーを使用します。これは、CSRFトークンのためにサーバーサイドの状態を必要としないので、実装が簡単です。CSRF トークンは、この方法を使用する場合、実際には標準的な認証クッキーである可能性があり、この値は通常どおりリクエストとともにクッキーを介して送信されますが、値は隠されたフィールドまたはヘッダーで繰り返され、攻撃者はそもそも値を読むことができないので複製することはできません。しかし、認証クッキー以外の別のクッキーを選択し、認証クッキーがHttpOnlyとマークされることで保護されるようにすることが推奨されます。これが、Cookieベースの方法でCSRFを防止するもう一つの一般的な理由です。
関連
-
[解決済み] HTTPの "Host "ヘッダーとは何ですか?
-
[解決済み] ブラウザのCookieドメインはどのように機能するのですか?
-
[解決済み] HTTPリダイレクト:301(永久)と302(一時)の比較
-
[解決済み] ブラウザの「F5」や「Ctrl + F5」によるリフレッシュはどのようなリクエストを発生させるのでしょうか?
-
[解決済み] Internet ExplorerのIFRAMEでCookieがブロックされる/保存されない
-
[解決済み] 認証とセッション管理に関するSPAのベストプラクティス
-
[解決済み】HTTPのPOSTとPUTの違いは何ですか?
-
[解決済み】Axiosがリクエストに含まれるクッキーを自動的に送信するようにする
-
[解決済み】JWTをブラウザに保存する場所は?CSRFから保護する方法は?
-
[解決済み】セッションは本当にRESTfulnessに違反するのか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] JavaScriptでウェブページのHTTPヘッダーにアクセスする
-
[解決済み] Chromeのネットワークデバッガをリダイレクトで使用する方法
-
[解決済み】AngularでHTTPリクエストにURL引数(クエリ文字列)を渡すには?
-
[解決済み】HTTP 1.0 vs 1.1
-
[解決済み】X-Requested-Withヘッダーのポイントは何でしょうか?
-
[解決済み】URLのアンパサンドのエスケープについて
-
[解決済み】「HTTPはステートレスプロトコルである」と言われているのはなぜですか?
-
[解決済み】HTTP 301と308のステータスコードの違いは何ですか?
-
[解決済み] PragmaヘッダーとCache-Controlヘッダーの違い?
-
[解決済み] HTTPのPOSTメソッドをキャッシュすることは可能ですか?