登录请求验证令牌问题


69

我一直在经历一个开发项目的错误日志,发现下面的错误(更名为保护有罪无罪) -

提供的防伪令牌是为用户“”提供的,但当前用户为“ admin”。

要复制这个问题并不是特别困难-

  1. 在登录页面打开应用程序
  2. 登录之前,请在同一台计算机上的同一浏览器中,在登录页面上打开另一个窗口或选项卡。
  3. 在第一个窗口中登录(或者实际上在第二个窗口中,顺序无关紧要)
  4. 尝试在剩余的登录窗口中登录

堆栈跟踪为-

System.Web.Mvc.HttpAntiForgeryException(0x80004005):提供的防伪令牌是给用户“”的,但当前用户是“ admin”。在System.Web.Helpers.AntiXsrf.AntiForgeryWorker.Validate(HttpContextBase httpContext)(System.Web.Helpers.AntiXsrf.TokenValidator.ValidateTokens(HttpContextBase httpContext,IIdentity身份,AntiForgeryToken sessionToken,AntiForgeryToken fieldToken)在System.Web.Helpers.AntiForgery。在System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters(ControllerContext controllerContext,IList`1过滤器,ActionDescriptor actionDescriptor)在System.Web.Mvc.Async.AsyncControllerActionInvoker处的System.Web.Mvc.ValidateAntiForgeryTokenAttribute.OnAuthorization(AuthorizationContext filterContext)处的Validate()。 <> c__DisplayClass25。

登录方法签名为-

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

这与Internet“ ASP.NET MVC 4 Web应用程序”模板项目中的方法签名完全相同,这表明Microsoft认为ValidateAntiForgeryToken是必要/最佳实践,或者只是在此处添加了属性,因为使用了该属性。其他地方。

显然,没有什么我可以做的来处理这个问题这种方法没有达到它的ValidateAntiForgeryToken是预请求过滤器和到达控制器之前就阻止该请求。

我可以在提交表单之前检查用户是否通过Ajax进行了身份验证,并尝试将其重定向到他们,或者只是删除了属性。

问题是这样的-我知道令牌的设计在用户已经针对您的站点进行身份验证时阻止来自另一个站点(CSRF)的请求,因此,将其从按定义使用的表单中删除是一个问题未经身份验证的用户

大概在这种情况下,该属性旨在减轻为您的应用程序提供伪造登录表单的恶意行为者的影响(尽管在抛出异常时,大概是用户已经输入了将被记录的详细信息,但可能会向他们发出警报)是错的)。否则,从外部站点向表单提交不正确的凭据将导致与站点本身完全相同的结果吗?我不依靠客户端验证/卫生来清理潜在的不安全输入。

是否有其他开发人员遇到过此问题(或者我们是否有非常有创造力的用户),如果是,您如何解决/缓解它?

更新:此问题完全存在于MVC5中,现在出现错误消息“提供的防伪令牌是针对与当前用户不同的基于声明的用户”。使用默认模板和身份提供程序时。微软开发人员传播者和Troy的PluralSight研究员Adam Tuliper在登录页面上的反伪造令牌中有一个相关的问题和有趣的答案,建议您删除该令牌。

Answers:


62

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

  1. 两种登录表单均加载相同的__RequestVerificationToken cookie(在同一DOM中只是同一集合)和相同的__RequestVerificationToken隐藏字段。这些令牌被键入匿名用户。
  2. 登录表单A带有上述令牌的帖子,对其进行验证,然后返回一个身份验证cookie,该cookie现在位于浏览器DOM中
  3. 登录表单B使用上述令牌发布,现在它也使用登录表单A中设置的auth cookie进行发布。

问题在于令牌无法在步骤3中进行验证,因为令牌已被密钥给匿名用户,但是已通过身份验证的用户在请求中传递了令牌。这就是为什么您看到此错误的原因:提供的防伪令牌用于用户“”,但当前用户是“ admin”

您只有这个问题,因为表单B在表单A发布之前就已加载,因此表单B预计将由匿名用户发布。

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

反伪造令牌所防范的主要潜在风险是CSRF,它通常利用经过身份验证的用户,因为其浏览器可能被诱骗发出任何请求,都会自动伴随有身份验证cookie,因此将在其上执行操作代表。在登录表单上不存在这种风险,因为通常不对用户进行身份验证,最糟糕的CSRF情况是伪造登录然后失败。您并不能代表用户完全转移资金!

但是,防伪令牌还有其他优点:例如,它可以防止暴力攻击实际执行该方法并击中数据库。您需要决定是否较少担心此问题,而更担心问题中遇到的情况。或者,或者您需要进入某个地方的请求管道,如果在进行防伪验证之前请求中已经存在身份验证cookie,则应采取措施。

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


1
谢谢特洛伊。我了解了这个问题(简单明了的消息),并且我们确实使用了“自定义错误”页面(在这种情况下,我从日志(Elmah)中提取了堆栈跟踪)。我无法解决的是从该方法中删除属性的风险(该属性已广泛用于所有导致应用程序中其他位置的数据更改的操作)。问题是,这是由销售人员完成的,我可以很容易地再次看到它的发生-尽管我可以通过简要介绍该问题来减轻这种情况-而且立即发现一个错误似乎也不是一件好事。销售演示。
2013年

4
我同意这是利基市场,并向项目负责人描述。问题是,如果有一百分之一的人出现在潜在客户面前,而错误是可以避免的,那么无论销售结果如何,我们都会受到批评,除非有充分的理由将属性保留在那里。这是通常仅内部使用的应用程序,因此暴力破解的机会很小。
2013年

1
请注意,如果从“登录”页面删除CSRF保护,则允许攻击者使用他们控制的帐户登录用户。当“用户控制的登录后页面”中存在持续的XSS漏洞时,我已经看到了这一漏洞。即攻击者1)登录,2)将XSS有效负载放在他可以编辑的页面中,3)使用CSRF请求在用户上触发该XSS有效负载(这只是让用户使用其真实身份登录的一种情况帐户)
Dinis Cruz 2013年

1
@DinisCruz和Troy,我在两周的时间里进行了仔细的思考,发现重复提交两次请求,在登录之前打开多个登录页面或可能使用返回按钮返回登录页面时引起的重复异常(我有也可能是原因)。在我们的应用程序上下文中,并且在所有其他POST方法上使用了令牌,我已经决定了没有令牌进行登录的风险很小。尽管在浏览器支持的地方,是否可以检查Origin报头是否与登录POST上的请求主机匹配,从而进一步降低任何风险?
2013年

7
只是来自现场的一些数据:我们在生产中遇到了这个问题,尽管我们将其视为边缘情况,但它的发生频率比我们预期的要高得多,每天从相对有限的用户群中出现多次。FWIW,我认为这可能是“普通用户”在其浏览器中执行“开发者用户”永远不会做的事情之一。
centralscru 2014年

13

针对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来关闭此功能Global.asax文件内的Application_Start方法中。

当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);
  }
}

AntiForgeryConfig自动完成功能无法选择SuppressIdentityHeuristicChecks。我正在使用Asp .NET Web API
DevEng

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

我在这里错过明显的东西吗?如果没有任何暗示,我将删除此答案。


我更喜欢这种解决方案;这更实用。
Mojtaba

我可能找到了一些东西:您可能想检查HttpContext中经过身份验证的用户是否与LoginModel中的用户匹配。否则,将触发此异常,如此处讨论的CSRF登录攻击:security.stackexchange.com/questions/2120/…会导致已登录的用户看到“警告”页面,而不是自定义错误页面。很好,用户认为他们使用自己的帐户登录!但是您的日志记录系统将记录无害的错误而不是攻击。我认为。
nmit026'1

@ nmit026我认为您是正确的。我也许可以测试这种情况,并查看是否可以添加条件来查看它是否可以为其他用户检测CSRF并记录错误。
kamranicus

-4

我有同样的问题,我通过将machineKey添加到system.web>来解决了

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

这是一个生成唯一Machnie密钥的 网站http://www.developerfusion.com/tools/generatemachinekey/

效果很好,我知道“登录”页面中可能不需要反伪造,但是如果有人确定要入侵您的网站,那么很高兴知道[ValidateAntiForgeryToken]可以阻止他们。


1
您能否解释一下为什么添加机器密钥可以解决此问题?
julealgon 2014年

1
此更改将无法解决此错误,因为它与同步的机器密钥可以防止的“无法验证防伪令牌”错误不同。
D-Sect 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.