登录请求验证令牌问题

时间:2013-09-30 14:25:45

标签: asp.net-mvc asp.net-mvc-4 request-validation

我一直在浏览开发项目的错误日志并发现以下错误(名称已更改以保护有罪无辜) -

  

提供的防伪令牌适用于用户“”,但目前   用户是“admin”。

这不是一个特别难以复制的问题 -

  1. 在登录页面打开应用程序
  2. 登录前,在同一台计算机上的同一浏览器中打开第二个窗口或标签到登录页面
  3. 登录第一个窗口(或者第二个,订单无关紧要)
  4. 尝试登录剩余的登录窗口
  5. 堆栈跟踪是 -

      

    System.Web.Mvc.HttpAntiForgeryException(0x80004005):提供   防伪令牌适用于用户“”,但当前用户是   “管理员”。在   System.Web.Helpers.AntiXsrf.TokenValidator.ValidateTokens(HttpContextBase   httpContext,IIdentity身份,AntiForgeryToken sessionToken,   AntiForgeryToken fieldToken)在   System.Web.Helpers.AntiXsrf.AntiForgeryWorker.Validate(HttpContextBase   httpContext)位于System.Web.Helpers.AntiForgery.Validate()处   System.Web.Mvc.ValidateAntiForgeryTokenAttribute.OnAuthorization(AuthorizationContext   filterContext)at   System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters(ControllerContext   controllerContext,IList`1过滤器,ActionDescriptor actionDescriptor)   在   System.Web.Mvc.Async.AsyncControllerActionInvoker<> 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是必要/最佳实践,或者只是在此处添加了属性,因为它被其他地方使用。

    显然,我无法在这个方法中处理这个问题,因为它没有到达,ValidateAntiForgeryToken是一个预请求过滤器,它在阻止请求到达控制器之前

    我可以在提交表单之前检查用户是否通过Ajax进行身份验证,然后尝试重定向到这些表单,或者只是删除该属性。

    问题是这个 - 我知道该令牌的设计是为了防止用户已经针对您的网站进行身份验证时来自其他网站(CSRF)的请求所以基于此是一个问题是从未经身份验证的用户定义的表单中删除它?

    据推测,此实例中的属性旨在缓解为您的应用程序提供虚假登录表单的恶意行为者(尽管在抛出异常时,可能是用户已经输入了他/她将要记录的详细信息 - 但它可能会提醒他们有些事情是错误的)。否则,从外部站点向表单提交不正确的凭据将导致与站点本身完全相同的结果?我不依赖客户验证/卫生来清理可能不安全的输入。

    是否有其他开发人员遇到过这个问题(或者我们是否有非常有创意的用户),如果是这样,您如何解决/减轻它?

    更新:此问题仍然存在于MVC5中,完全是故意的,现在出现错误消息“提供的防伪令牌适用于与当前用户不同的基于声明的用户。”使用默认模板和身份提供程序时。 Microsoft Developer Evangelist和Troy的同事PluralSight作者Adam Tuliper在Anti forgery token on login page提出了一个相关的问题和有趣的答案,建议只需删除令牌。

4 个答案:

答案 0 :(得分:60)

我刚刚在ASafaWeb上测试了这个模式,结果相同(它使用相同的默认实现)。这就是我认为正在发生的事情

  1. 两个登录表单都加载了相同的__RequestVerificationToken cookie(它只是在同一个DOM中设置的相同)和相同的__RequestVerificationToken隐藏字段。这些令牌是匿名用户的密钥。
  2. 登录表单包含上述令牌的帖子,验证它们然后返回一个现在在浏览器DOM中的身份验证cookie
  3. 登录表单B使用上述令牌发布 ,但是 现在它也使用登录表单A中的auth cookie设置发布。
  4. 问题是令牌在步骤3中没有验证,因为它是匿名用户的键,但是经过身份验证的用户在请求中传递了令牌。这就是您看到错误的原因:提供的防伪令牌适用于用户“”,但当前用户为“admin”

    你只是遇到了这个问题,因为在表单A发布之前加载了表单B因此表单B期望由匿名用户发布。

    从未经身份验证的用户定义的表单中删除它是否存在问题?

    防伪令牌防范的主要潜在风险是CSRF,通常利用经过身份验证的用户,因为他们的浏览器被欺骗发出的任何请求都将自动伴随auth cookie因此将代表他们执行操作。登录表单上不存在此风险,因为通常用户未经过身份验证,这里最糟糕的CSRF案例是登录伪造然后失败;你并没有真正代表用户转账!

    防伪令牌还有其他优点:例如,它可以防止暴力攻击实际执行该方法并击中数据库。您需要决定是否不再担心这一点,并且更担心您在问题中遇到的情况。要么你需要在某个地方下拉到请求管道中,如果在防伪验证发生之前请求中已经存在auth cookie,那就采取行动。

    但坦率地说,我不确定我是否看到了这个问题;为了重现这个问题,用户必须同时打开多个登录表单,然后尝试连续登录每个登录表单 - 这真的要发生吗?当它确实发生时,第二次登录返回一个自定义错误页面(当然你会在生产中做)真的很重要吗?恕我直言,保留默认行为。

答案 1 :(得分:13)

针对AntiForgeryToken运行的验证代码还会检查您登录的用户凭据是否未更改 - 这些也在Cookie中加密。这意味着,如果您在弹出窗口或其他浏览器选项卡中登录或注销,则表单提交将失败,并出现以下异常:

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

您可以在 Global.asax文件中的 Application_Start 方法中添加 AntiForgeryConfig.SuppressIdentityHeuristicChecks = true; 来关闭此功能。

当AntiForgeryToken未验证时,您的网站将抛出类型为 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);
  }
}

答案 2 :(得分:10)

好的,如果我的假设不正确,请纠正我。

当抛出此异常时,您更愿意让用户继续,同时也可能显示相关的消息/警告?因为当抛出此异常时,我们已经可以知道当前用户是否经过身份验证 - 我们可以访问该请求。

那么,为什么这不可以接受呢?

[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");
    }
}

我错过了一些明显的东西吗?如果它有一些我没有看到的暗示,我会删除这个答案。

答案 3 :(得分:-4)

我有同样的问题,我通过添加machineKey来解决它     的System.Web&GT;

<machineKey validationKey="your machine key numbers" 
decryptionKey="Your description key nuumbers" validation="SHA1" decryption="AES" />

这是一个生成独特Machnie Keys的网站     http://www.developerfusion.com/tools/generatemachinekey/

效果很好,我知道在登录页面中可能不需要Anti Forgery,但是如果有人决定破解你的网站,那么很高兴知道[ValidateAntiForgeryToken]可以阻止它们。