login请求validation令牌问题

我一直在经历一个开发项目的错误日志,并发现以下错误(名称改为保护有罪无辜) –

提供的防伪标记是为了用户“”,但当前用户是“pipe理员”。

这不是一个特别难以重现的问题,

  1. 在login页面打开应用程序
  2. 在login之前,在同一台计算机上的 一个浏览器中打开第二个窗口或标签页至login页面
  3. 在第一个窗口login(或者其次,顺序无关紧要)
  4. 尝试login剩余的login窗口

堆栈跟踪是 –

System.Web.Mvc.HttpAntiForgeryException(0x80004005):提供的防伪标记用于用户“”,但当前用户是“admin”。 在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。在System.Web.Mvc.ValidateAntiForgeryTokenAttribute.OnAuthorization(AuthorizationContext filterContext)上System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters(System.Web.Mvc.Async.AsyncControllerActionInvoker的ControllerContext controllerContext,IList`1filter,ActionDescriptor actionDescriptor)上的Validate()。 c__DisplayClass25.b__1e(AsyncCallback asyncCallback,Object asyncState)

login方法签名是 –

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

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

很明显,我没有办法处理这个方法中的问题因为它没有到达,ValidateAntiForgeryToken是一个预先请求的filter,它在请求到达控制器之前阻塞了它。

在提交表单之前,我可以检查用户是否通过Ajax进行了validation,如果是,则尝试redirect到它们,或者直接删除该属性。

问题是这个 – 我知道这个标记是devise来防止来自另一个站点(CSRF)的请求,当用户已经对你的站点进行了authentication的时候,所以在这个基础上把它从一个定义将被使用的表单通过未经authentication的用户

据推测,这个实例中的属性旨在减轻为您的应用程序提供虚假login表单的恶意行为者(尽pipe在抛出exception的时候,用户可能已经input了他或她将要logging的详细信息,但可能会提醒他们是错的)。 否则,从外部网站向表单提交不正确的凭证将导致与网站本身的结果完全相同的结果? 我不是依靠客户端validation/卫生来清理潜在的不安全的input。

有其他开发者遇到这个问题(或者我们有非凡的创造性的用户),如果是的话,你是如何解决/缓解它?

更新:这个问题仍然存在于MVC5,完全故意,现在与错误消息“所提供的防伪令牌是针对不同的基于声明的用户比当前用户”。 当使用默认的模板和身份提供者。 Microsoft Developer Developer Evangelist和Troy的同事PluralSight的作者Adam Tuliper在login页面上的反伪造令牌有一个相关的问题和有趣的答案,build议删除令牌。

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

  1. 两个login表单都使用相同的__RequestVerificationToken cookie(它只是在同一个DOM中设置的同一个)以及相同的__RequestVerificationToken隐藏字段。 这些令牌键入匿名用户。
  2. login表单具有上述标记的post,validation它们,然后返回一个现在在浏览器DOM中的授权cookie
  3. login表格B的post包含上述标记,但是现在也会使用login表单A的auth cookie进行发布。

问题是令牌在步骤3中没有validation,因为它是键入匿名用户的密钥,但它是通过身份validation的用户在请求中传递的。 这就是为什么你看到的错误: 提供的防伪标记是为用户“”,但当前用户是“pipe理员”

你只有这个问题,因为表单B加载之前表单A张贴,因此表单B期望由匿名用户张贴。

将它从一个根据定义将被未经validation的用户使用的表单中删除是一个问题吗?

防伪令牌的主要潜在风险是CSRF,它通常利用经过authentication的用户,因为任何浏览器的请求都可能被欺骗发出,并自动伴随着一个auth cookie,因此这个动作将会在他们的代表。 这种风险在login表单上不存在,因为通常用户没有通过authentication,这里最糟糕的CSRF情况是login被伪造然后失败; 你不完全是代表用户转移资金!

但是,防伪标记还有其他一些优点:例如,防止实际执行方法和击中数据库的powershell攻击。 你需要决定你是否不那么担心,更担心你遇到的问题。 无论是或者你需要下拉到请求pipe道的地方,并采取行动,如果在防伪validation发生之前,请求中已经存在一个validationcookie。

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

针对AntiForgeryToken运行的validation代码还会检查您login的用户凭证是否未更改 – 这些还在Cookie中encryption。 这意味着如果您在popup式窗口或其他浏览器选项卡中login或注销,表单提交将失败,并出现以下例外情况:

 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不validation你的网站会抛出一个exceptiontypesSystem.Web.Mvc.HttpAntiForgeryException 。 通过捕获HttpAntiForgeryException ,您至less可以为用户提供更多信息丰富的页面,从而使这种情况更容易一些

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

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

当抛出这个exception时,你希望让用户继续,同时也可能显示相关的消息/警告? 因为当这个exception被抛出时,我们已经可以知道当前用户是否被authentication – 我们可以访问请求。

那么,为什么不能接受呢?

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

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

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

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

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

很好的工作,据我所知,反login可能不需要在login页面,但如果有人决心侵入你的网站,安慰知道[ValidateAntiForgeryToken]是在那里阻止他们。