web-dev-qa-db-ja.com

ログイン要求検証トークンの問題

開発プロジェクトのエラーログを調べたところ、次のエラーが見つかりました(名前を変更して、 有罪 無実)-

提供された偽造防止トークンはユーザー「」を対象としていましたが、現在のユーザーは「admin」です。

これは特に再現が難しい問題ではありませんでした。

  1. ログインページでアプリケーションを開きます
  2. ログインする前に、same browserの2番目のウィンドウまたはタブをsame computerのログインページに開きます。
  3. 最初のウィンドウでログインします(実際、2番目のウィンドウでは順序は関係ありません)
  4. 残りのログインウィンドウでログインを試みます

スタックトレースは

System.Web.Mvc.HttpAntiForgeryException(0x80004005):提供された偽造防止トークンはユーザー ""用でしたが、現在のユーザーは "admin"です。 System.Web.Helpers.AntiXsrf.TokenValidator.ValidateTokens(HttpContextBase httpContext、IIdentity identity、AntiForgeryToken sessionToken、AntiForgeryToken fieldToken)at System.Web.Helpers.AntiXsrf.AntiForgeryWorker.Validate(HttpContextBase httpContext)at System.Web.Helper.Anti System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters(ControllerContext controllerContext、IList`1 filters、ActionDescriptor actionDescriptor)のSystem.Web.Mvc.Async.AsyncControllerActionInvokerのSystem.Web.Mvc.ValidateAntiForgeryTokenAttribute.OnAuthorization(AuthorizationContext filterContext)のValidate() <> c__DisplayClass25.b__1e(AsyncCallback asyncCallback、Object asyncState)

ログインメソッドの署名は

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
...
}

これは、インターネット「ASP.NET MVC 4 Webアプリケーション」テンプレートプロジェクトのメソッドのシグネチャとまったく同じです。これは、MicrosoftがValidateAntiForgeryTokenが必要/ベストプラクティスであると感じたか、使用されたためにここに属性を単に追加したことを示します他のどこでも。

明らかに問題を処理するために私ができることは何もありませんwithinこのメソッドは到達していないため、ValidateAntiForgeryTokenは事前要求フィルターであり、コントローラーに到達する前に要求をブロックしています。

フォームを送信する前にユーザーがAjaxを介して認証されているかどうかを確認し、認証されている場合はリダイレクトするか、単に属性を削除します。

質問はこれです-トークンは、ユーザー既にサイトに対して認証されているであるため、別のサイト(CSRF)からのリクエストを防ぐように設計されていることを理解しています定義上、認証されていないユーザーが使用するフォームから削除するのは問題ですか?

おそらく、このインスタンスの属性は、アプリケーションに偽のログインフォームを提供する悪意のあるアクターを緩和するように設計されています(ただし、例外がスローされるまでに、ユーザーは既に記録されている詳細を入力しているはずですが、何かを警告する可能性があります)間違っている)。それ以外の場合、外部サイトからフォームに誤った資格情報を送信すると、サイト自体とまったく同じ結果になりますか?安全でない可能性のある入力をクリーンアップするために、クライアントの検証/衛生に頼っていません。

他の開発者がこの問題に遭遇しましたか(または異常に創造的なユーザーがいますか)、そうであれば、どのように解決/緩和しましたか?

Update:この問題はMVC5に存在しますが、完全に意図的に、「提供された偽造防止トークンは現在とは異なるクレームベースのユーザー向けでした」というエラーメッセージが表示されますユーザー。"デフォルトのテンプレートとIDプロバイダーを使用する場合。 ログインページの偽造防止トークン で、Microsoft開発者エバンジェリストおよびトロイの仲間PluralSight作成者Adam Tuliperから関連する質問と興味深い回答があります。

67
pwdst

ASafaWeb でこのパターンをテストしたところ、同じ結果が得られました(同じデフォルト実装を使用しています)。これが私が信じていることです

  1. 両方のログインフォームは、同じ__RequestVerificationToken Cookie(同じDOMに設定されているのと同じもの)と同じ__RequestVerificationToken隠しフィールドでロードされます。これらのトークンは、匿名ユーザーにキー設定されます。
  2. ログインフォームAは上記のトークンを使用して投稿し、それらを検証してから、ブラウザDOMにある認証Cookieを返します
  3. ログインフォームBは、上記のトークンを使用してalsoを投稿しますが、現在、認証Cookieを使用して投稿していますログインフォームAからも設定します。

問題は、トークンが匿名ユーザーにキー入力されているが、認証されたユーザーによってリクエストで渡されているため、ステップ3でトークンが検証されないことです。これが、エラーが表示される理由です:提供された偽造防止トークンはユーザー「」用でしたが、現在のユーザーは「admin」です

この問題は、フォームBがフォームAが送信される前にロードされたため、フォームBが匿名ユーザーによって投稿されることを想定しているために発生します。

定義から認証されていないユーザーが使用するフォームから削除することは問題ですか?

偽造防止トークンが保護する主な潜在的リスクはCSRFです。これは、ブラウザがリクエストを発行してintoすことができるため、認証済みユーザーをusuallyが利用します。自動的に認証Cookieが添付されるため、アクションはそれらに代わって実行されます。通常、ユーザーは認証されないため、ログインフォームにはこのリスクは存在しません。ここでの最悪のCSRFケースは、ログインが偽造されて失敗することです。ユーザーに代わって正確に送金するわけではありません!

偽造防止トークンには他にも利点があります。たとえば、実際にメソッドを実行してDBにヒットするブルートフォース攻撃を防止します。あなたはこれについてあまり心配していないか、あなたの質問で遭遇しているシナリオについてもっと心配しているかどうかを決める必要があります。それとも、偽造防止の検証が行われる前に認証Cookieがリクエストに既に存在する場合は、どこかのリクエストパイプラインにドロップダウンしてアクションを実行する必要があります。

率直に言って、私は問題を見るかどうかわかりません。この問題を再現するには、ユーザーは複数のログインフォームを同時に開いて、それぞれに連続してログインしてみなければなりません。これは本当に心配するほど起こるのでしょうか?そして、それが起こった場合、2回目のログインがカスタムのエラーページを返すことは本当に重要ですか?(もちろん本番で行います)私見、デフォルトの動作のままにします。

61
Troy Hunt

AntiForgeryTokenに対して実行される検証コードは、ログインしているユーザーの資格情報が変更されていないことも確認します。これらもCookieで暗号化されます。これは、ポップアップまたは別のブラウザタブでログインまたはログアウトした場合、次の例外でフォームの送信が失敗することを意味します。

System.Web.Mvc.HttpAntiForgeryException (0x80004005):
The provided anti-forgery token was meant for user "", but the current user is "SomeOne".

AntiForgeryConfig.SuppressIdentityHeuristicChecks = true; in Application_StartメソッドをGlobal.asaxファイルに入れることで、これをオフにできます。

AntiForgeryTokenが検証されない場合、WebサイトはタイプSystem.Web.Mvc.HttpAntiForgeryExceptionの例外をスローします。少なくともHttpAntiForgeryExceptionをキャッチすることで、これらの例外をターゲットとしたより有益なページをユーザーに提供することで、これを少し簡単にできます。

private void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();

if (ex is HttpAntiForgeryException)
  {
    Response.Clear();
    Server.ClearError(); //make sure you log the exception first
    Response.Redirect("/error/antiforgery", true);
  }
}
13
Zia Ul Mustafa

さて、私の仮定が間違っている場合は修正してください。

この例外がスローされた場合、関連するメッセージ/警告を表示しながら、ユーザーが続行できるようにすることをお勧めしますか?この例外がスローされると、現在のユーザーが認証されているかどうかを既に知ることができるため、リクエストにアクセスできます。

それでは、なぜこれが受け入れられないのでしょうか?

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
...
}

/// <summary>
/// Handle HttpAntiForgeryException and redirect if user is already authenticated
/// </summary>
/// <param name="filterContext"></param>
/// <remarks>
/// See: http://stackoverflow.com/questions/19096723/login-request-validation-token-issue
/// </remarks>
protected override void OnException(ExceptionContext filterContext)
{
    base.OnException(filterContext);

    var action = filterContext.RequestContext.RouteData.Values["action"] as string;
    var controller = filterContext.RequestContext.RouteData.Values["controller"] as string;

    if ((filterContext.Exception is HttpAntiForgeryException) &&
        action == "Login" &&
        controller == "MyController" &&
        filterContext.RequestContext.HttpContext.User != null &&
        filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated)
    {
        LogManager.GetCurrentClassLogger().Warn(
            "Handled AntiForgery exception because user is already Authenticated: " +
                filterContext.Exception.Message, filterContext.Exception);

        filterContext.ExceptionHandled = true;

        // redirect/show error/whatever?
        filterContext.Result = new RedirectResult("/warning");
    }
}

ここで明らかな何かを見逃していますか?表示されていない意味がある場合は、この回答を削除します。

10
kamranicus