web-dev-qa-db-ja.com

MVC 4アプリケーションでHttpAntiForgeryExceptionを適切に処理する方法

ここにシナリオがあります:

ログインページがあります。ユーザーが署名すると、ホームアプリケーションページにリダイレクトされます。次に、ユーザーはブラウザの戻るボタンを使用しており、ログインページに移動しています。彼は再度ログインを試みますが、今は例外がスローされます:

HttpAntiForgeryException(0x80004005):指定された偽造防止トークンはユーザー ""向けでしたが、現在のユーザーは "userName"です。

これはキャッシングに関連していることを知っています。必要なすべてのヘッダー(no-cache、no-store、must-revalidateなど)を設定するカスタムNoCacheフィルターを使用して、ログインアクションのブラウザーキャッシュを無効にしました。

  • これはすべてのブラウザで機能しているわけではありません
  • 特にSafari(ほとんどの場合モバイル)はこのような設定を完全に無視します

私はハックを作ってサファリモバイルを強制的に更新しようとしますが、これは私が期待していることではありません。

私ができることを知りたいです:

  • ユーザーに問題がないことを示すことなく例外を処理します(ユーザーには完全に透過的)
  • 偽造防止トークンのユーザー名を置き換えることでこの問題を防止これにより、ブラウザーのキャッシュに関連するハックが次のバージョンのブラウザーで機能しなくなる場合に、この例外なしでユーザーが再びログインできるようになります。
  • それぞれが異なる動作をするため、ブラウザーの動作に依存しないようにしたいと思います。

更新1

明確にするために、MVCでエラーを処理する方法を知っています。問題は、このエラー処理が私の問題をまったく解決していないことです。エラー処理の基本的な考え方は、Niceメッセージを含むカスタムエラーページにリダイレクトすることです。しかし、私はこのエラーが発生するのを防ぎたいのですが、ユーザーに見える方法でそれを処理するのではありません。ハンドルとは、ユーザー名を置き換えたり、その他の適切なアクションを行ってから、ログインを続行することを意味します。

更新2

私のために働いているソリューションを以下に追加しました。

35
Marcin

しばらく調査した後、ユーザーのためにこのエラーを取り除く方法をいくつか見つけたと思います。完璧ではありませんが、少なくともエラーページは表示されません。

HandleErrorAttributeに基づいてフィルターを作成しました:

    [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", 
        Justification = "This attribute is AllowMultiple = true and users might want to override behavior.")]
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
    public class LoginAntiforgeryHandleErrorAttribute : FilterAttribute, IExceptionFilter
    {
        #region Implemented Interfaces

        #region IExceptionFilter

        /// <summary>
        /// </summary>
        /// <param name="filterContext">
        /// The filter context.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public virtual void OnException(ExceptionContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }

            if (filterContext.IsChildAction)
            {
                return;
            }

            // If custom errors are disabled, we need to let the normal ASP.NET exception handler
            // execute so that the user can see useful debugging information.
            if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
            {
                return;
            }

            Exception exception = filterContext.Exception;

            // If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method),
            // ignore it.
            if (new HttpException(null, exception).GetHttpCode() != 500)
            {
                return;
            }

            // check if antiforgery
            if (!(exception is HttpAntiForgeryException))
            {
                return;
            }

            filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary
                {
                    { "action", "Index" }, 
                    { "controller", "Home" }
                });

            filterContext.ExceptionHandled = true;
        }

        #endregion

        #endregion
    }

次に、このフィルタをログインに適用しましたPOSTアクション:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
[LoginAntiforgeryHandleError]
public ActionResult Login(Login model, string returnUrl)
{

このソリューションの主なアイデアは、偽造防止の例外をメインのインデックスアクションにリダイレクトすることです。ユーザーがまだ認証されていない場合は表示されますloginページユーザーが既に認証されている場合はindexページが表示されます.

PDATE 1このソリューションには1つの潜在的な問題があります。誰かが別の資格情報でログインしている場合、エラー時に追加のログインランタイムを追加する必要があります-以前のユーザーをログアウトして新しいユーザーをログインさせます。このシナリオは処理されません。

19
Marcin

影響を受ける機能が1つまたはいくつかしかない場合は、フィルターを作成するのが少し技術的に行き過ぎかもしれません。より単純ですが一般的ではない解決策は、特定のメソッドの[ValidateAntiForgeryToken]を単に削除し、ユーザーがログインしているかどうかを確認した後に手動検証を追加することです。

if (User.Identity.IsAuthenticated)
{
    return RedirectToAction("Index", "Home");
}
System.Web.Helpers.AntiForgery.Validate();
/* proceed with authentication here */
19
Crypth

エラーを処理するアクションフィルターを追加することで、例外を処理できるはずです。

[HandleError(View="AntiForgeryExceptionView", ExceptionType = typeof(HttpAntiForgeryException))]

Todoなので、web.configでカスタムエラーがオンになっていることを確認してください。

<customErrors mode="On"/>

エラー処理の詳細については、この blog も参照してください。

EditMVC4を使用していて、ブログはMVC3に関するものなので、 MSDNライブラリ-HandleErrorAttribute も確認できます。 、しかしバージョンは実際に違いを生むべきではありません。

5
Jos Vinke

古い質問ですが、今日この問題に遭遇しました。解決方法は、次のようにログオフアクションにリダイレクトすることでした。

public ActionResult Login(string returnUrl) 
{
    if (WebSecurity.IsAuthenticated)
        return RedirectToAction("LogOff");

    ...
}
1
Robin Dorbell