web-dev-qa-db-ja.com

RequestVerificationTokenが一致しません

アンチCRSF MVCメカニズムに問題があります。 Cookieと返されたフォーム入力が一致しません。特定のページでのみ、毎回エラーが発生します。アプリケーションの残りの部分ではうまく機能します。

サーバーはHTTP 500 Internal Server Errorを返しています。ログでこの例外を確認できます。

[System.Web.Mvc.HttpAntiForgeryException]:{「必要な偽造防止トークンが提供されなかったか、無効でした。」}

これは、サーバーが生成している非表示の入力です。

<input name="__RequestVerificationToken" type="hidden" value="QK8P7rjyZE6Vm5seY7Fr704YCOoFGdTIMzl1W7R0ZFpXSMjGKLG2T05DfFSYTxvtQCEx7DDT69DGsDB2+ZXFHY8oAjiKz0gw8BhDFywgmfIpoXnGpj7fONNzIIfvbrDrE9WJsMu6Io/0bDLM5WfKs0zktiNjyOWpfYrmnfINYmjW8NLOZFoz74xTcgTptAld">

そして、これは返されるCookieです:

Set-Cookie:__RequestVerificationToken_L2VGbG93=skmTAVI8HCbfxDS+xhioIMIISL3UOBI7qJM1JbHjTtAqKl4W70pDUcTKMm0p3R3mrHDziE8vXw0C0OO4HArzWO1/e6py+v/cFdbe9maFgjl4jMiZ9Wc4YIhC6+IUXkk6yqJDJ8dCIr8qtGaYcD9IX+m7/SlVhu521KQSWJYRcaY=; path=/; HttpOnly

サーバーが送信しているものを調べると、Cookieはまったく同じですが、ペイロードのエンコードが異なっていると思います。

__RequestVerificationToken:QK8P7rjyZE6Vm5seY7Fr704YCOoFGdTIMzl1W7R0ZFpXSMjGKLG2T05DfFSYTxvtQCEx7DDT69DGsDB2%2BZXFHY8oAjiKz0gw8BhDFywgmfIpoXnGpj7fONNzIIfvbrDrE9WJsMu6Io%2F0bDLM5WfKs0zktiNjyOWpfYrmnfINYmjW8NLOZFoz74xTcgTptAld

違いは、エンコードされているように見える2つの文字にあります。

    /    ->   %2F  
    +    ->   %2B

これらは、非表示の入力フィールドと投稿ペイロードの間で見つけることができる唯一の違いです。

ValidateAntiForgeryTokenがトークンの検証に失敗する原因となっている問題は何でしょうか?

よろしく。

34
vtortola

最近ValidateAntiForgeryTokenの問題をいくつか解決しましたので、調査結果を共有します。

Salt:これは単一のページでのみ発生すると言及しているため、私の推測では、呼び出しで異なるsalt値を使用しているHtml.AntiForgeryToken(salt)およびValidateAntiForgeryToken(salt)呼び出し。

[〜#〜] ajax [〜#〜]:別の答えが述べたように、AJAXトークンがPOSTに確実に含まれるようにします。これが私のお気に入りです すべてのAJAX POST=要求
しかし、質問では、トークンが送信されていることを確認したと述べています。トークンを1回だけ送信していることを確認しましたか?私のAJAX私の呼び出しはトークンを2回送信し、それが値を結合して失敗する原因になっていることがわかりました。

マシンキーとCookie:この問題は見苦しく、簡単に見つけることができます(例外が発生します)が、あまり直感的ではありません。検証Cookieとトークンは、一意の「マシンキー」を使用してエンコードおよびデコードされます。つまり、サーバーファームがある場合、またはサーバーを変更した場合、Cookieは無効になります。ブラウザを閉じると問題が修正されます(CookieはセッションCookieであるため)。ただし、ブラウザウィンドウを長時間バックグラウンドで開いたままにする人もいます。
解決策は、構成ファイルに「マシンキー」を設定することです。これにより、すべてのサーバーで同じキーを使用するようMVCに指示し、Cookieがどこでも復号化できるようにします。

エンコーディングのバグ:jMeterというテストユーティリティを使用して、ページのロードテストを試みましたが、トークンの原因となったバグがあることがわかりました。余分な2つの"値の周り。
解決策は、ツールに対する信頼を下げることです!ブラウザでテストし、機能する場合は、トークンとCookieの値を抽出するテストを作成し、ブレークポイントを設定して結果を確認します。

これらのいずれも機能しない場合は、 ValidateAntiForgeryTokenAttributeのMVCソースコード 、特にOnAuthorizationメソッドを確認することをお勧めします。検証が失敗する可能性のあるさまざまな手順を確認するのに役立ちます。エラーのException.StackTraceどの部品が故障しているかを判断します。

サイドノートとして、MVCでのValidateAntiForgeryTokenの実装が本当に嫌いです:

  • 失敗する可能性のある検証手順は約5つありますが、一般的なエラーメッセージは1つだけです。
  • クラスは封印されているため、追加機能で拡張することはできません。
  • 暗号化方法は奇妙です-Pageを初期化し、トークンとCookieを暗号化するために人工のViewStateを作成します。やり過ぎのようです。

そのため、ソースコードを取得し、独自の特殊なサブクラスを作成しました。これは、検証メソッドにブレークポイントを設定でき、失敗した検証ステップを簡単に特定できるため、問題のデバッグにも非常に役立ちました。 。

77
Scott Rippey

これがAjaxリクエストとして送信されている場合、フレームワークの現在のセットアップは、これを自然に行うためにビルドされていません。

Luckly Phil Haakは、CSRFとAjaxの扱いに関する素晴らしいブログ記事を書きました-> AjaxでCSRFを防ぐ これは、既存のフレームワークを使用し、Ajax/Jsonで動作するように修正する方法について詳しく説明しています。

4
Skuld

最近の調査結果から...

コンテンツタイプをajaxリクエストで「application/x-www-form-urlencoded」に設定した場合、AFRTをデータに含める必要があります

コンテンツタイプを「application/json」に設定すると、トークンはhaackで説明されているようにajaxの「headers」プロパティに入ります。

サーバー上でフォームタイプトークンをチェックする場合、Vanilla AntiForgeryRequestTokenAttributeを使用しても問題ありませんが、ヘッダーで送信されたトークンを検証する場合は、AntiForgeryToken.OnAuthorize ...または何でも、トークンを渡す必要がありますcookie(httpコンテキスト)。

それは簡単ではありませんが、それがみんなだったらそれをやっているでしょう:)

2
ComeIn