pwd*_*dst 68 asp.net-mvc request-validation asp.net-mvc-4
我一直在浏览开发项目的错误日志,发现以下错误(更改名称以保护有罪的无辜者) -
提供的防伪令牌适用于用户"",但当前用户为"admin".
这不是一个特别难以复制的问题 -
堆栈跟踪是 -
System.Web.Mvc.HttpAntiForgeryException(0x80004005):提供的防伪令牌适用于用户"",但当前用户为"admin".在System.Web.Helpers.AntiForgery的System.Web.Helpers.AntiXsrf.TokenValidator.ValidateTokens(HttpContextBase httpContext,IIdentity identity,AntiForgeryToken sessionToken,AntiForgeryToken fieldToken)中的System.Web.Helpers.AntiXsrf.AntiForgeryWorker.Validate(HttpContextBase httpContext).在System.Web.Mvc.Mvc.Asvoke.Av响应(SystemContext控件),System.Web.Mvc.AsvokeAuthorizationFilters(ControllerContext controllerContext,IList`1过滤器,ActionDescriptor actionDescriptor)的System.Web.Mvc.ValidateAntiForgeryTokenAttribute.OnAuthorization(AuthorizationContext filterContext)中验证(),System.Web.Mvc.Async.AsyncControllerActionInvoker. <> c__DisplayClass25.
登录方法签名是 -
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
...
}
Run Code Online (Sandbox Code Playgroud)
这与互联网"ASP.NET MVC 4 Web应用程序"模板化项目中的方法签名完全相同,这表明Microsoft认为ValidateAntiForgeryToken是必要/最佳实践,或者只是在此处添加了属性,因为它已被使用在其他地方.
显然,我无法在此方法中处理问题,因为它没有到达,ValidateAntiForgeryToken是一个预请求过滤器,它在阻止请求到达控制器之前.
在提交表单之前,我可以检查用户是否通过Ajax进行了身份验证,如果是,则尝试重定向到这些用户,或者只是删除该属性.
问题是这个 - 我理解该令牌的设计是为了防止来自另一个站点(CSRF)的请求,当用户已经针对您的站点进行了身份验证时,所以基于这个问题将它从表单中删除是一个问题,根据定义将使用它未经验证的用户?
据推测,此实例中的属性旨在缓解为您的应用程序提供虚假登录表单的恶意行为者(尽管在抛出异常时可能已经输入了他或她已经记录的详细信息 - 但它可能会提醒他们某些事情是错的).否则,从外部站点向表单提交不正确的凭据将导致与站点本身完全相同的结果?我不依靠客户验证/卫生来清理可能不安全的输入.
让其他开发人员遇到此问题(或者我们是否拥有异常富有创意的用户)如果是这样,您如何解决/减轻它?
更新:此问题在MVC5中仍然存在,完全是故意的,现在出现错误消息"提供的防伪令牌是针对与当前用户不同的基于声明的用户".使用默认模板和身份提供程序时.Microsoft Developer Evangelist和Troy的同事PluralSight作者Adam Tuliper在登录页面上的Anti forgery 令牌中提出了一个相关的问题和有趣的答案,建议只需删除令牌.
Tro*_*unt 61
我刚刚在ASafaWeb上测试了这个模式,结果相同(它使用相同的默认实现).这就是我认为正在发生的事情
问题是令牌在步骤3中没有验证,因为它被键入匿名用户,但是被认证用户传递给请求.这就是您看到错误的原因:提供的防伪令牌适用于用户"",但当前用户是"admin"
您只有这个问题,因为表单B在表单A之前加载,因此表单B期望由匿名用户发布.
从未经身份验证的用户使用的表单中删除它是一个问题吗?
防伪令牌防范的主要潜在风险是CSRF,它通常利用经过身份验证的用户,因为他们的浏览器可以被欺骗发布的任何请求将自动伴随授权cookie,因此将对其执行操作代表.登录表单上不存在此风险,因为通常用户未经过身份验证,这里最糟糕的CSRF案例是登录伪造然后失败; 你并没有真正代表用户转账!
防伪令牌还有其他优点:例如,它可以防止暴力攻击实际执行该方法并命中数据库.您需要决定是否不再担心这一点,并且更担心您在问题中遇到的情况.或者您需要在某处下拉到请求管道中,并且如果在防伪验证发生之前请求中已存在auth cookie,则采取措施.
坦率地说,我不确定我是否看到了这个问题; 为了重现这个问题,用户必须同时打开多个登录表单,然后尝试连续登录每个登录表单 - 这真的要发生吗?当它确实发生时,第二次登录返回一个自定义错误页面(当然你会在生产中做)真的很重要吗?恕我直言,保留默认行为.
小智 13
针对AntiForgeryToken运行的验证代码还会检查您登录的用户凭据是否未更改 - 这些也在cookie中加密.这意味着,如果您在弹出窗口或其他浏览器选项卡中登录或注销,则表单提交将失败,并出现以下异常:
System.Web.Mvc.HttpAntiForgeryException (0x80004005):
The provided anti-forgery token was meant for user "", but the current user is "SomeOne".
Run Code Online (Sandbox Code Playgroud)
您可以通过放置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);
}
}
Run Code Online (Sandbox Code Playgroud)
kam*_*cus 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");
}
}
Run Code Online (Sandbox Code Playgroud)
我错过了一些明显的东西吗?如果它有一些我没有看到的暗示,我会删除这个答案.