asp.net-mvc 登录请求验证令牌问题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19096723/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Login request validation token issue
提问by pwdst
I have been going through the error logs of a development project and found the following error (name changed to protect the guiltyinnocent)-
一直在翻看一个开发项目的错误日志,发现如下错误(改名是为了保护有罪的无辜者)-
The provided anti-forgery token was meant for user "", but the current user is "admin".
提供的防伪令牌适用于用户“”,但当前用户是“admin”。
This was not an especially difficult issue to reproduce-
这并不是一个特别难以重现的问题——
- Open the application at the login page
- Open a second window or tab in the same browseron the same computerto the login page before logging in
- Login in the first window (or indeed second, the order doesn't matter)
- Attempt to login in the remaining login window
- 在登录页面打开应用程序
- 登录前在同一台计算机上的同一浏览器中打开第二个窗口或选项卡到登录页面
- 在第一个窗口登录(或者实际上是第二个,顺序无关紧要)
- 尝试在剩余的登录窗口中登录
The stack trace is-
堆栈跟踪是-
System.Web.Mvc.HttpAntiForgeryException (0x80004005): The provided anti-forgery token was meant for user "", but the current user is "admin". at System.Web.Helpers.AntiXsrf.TokenValidator.ValidateTokens(HttpContextBase httpContext, IIdentity identity, AntiForgeryToken sessionToken, AntiForgeryToken fieldToken) at System.Web.Helpers.AntiXsrf.AntiForgeryWorker.Validate(HttpContextBase httpContext) at System.Web.Helpers.AntiForgery.Validate() at System.Web.Mvc.ValidateAntiForgeryTokenAttribute.OnAuthorization(AuthorizationContext filterContext) at System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor) at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass25.b__1e(AsyncCallback asyncCallback, Object asyncState)
System.Web.Mvc.HttpAntiForgeryException (0x80004005):提供的防伪令牌适用于用户“”,但当前用户是“admin”。在 System.Web.Helpers.AntiXsrf.TokenValidator.ValidateTokens(HttpContextBase httpContext, IIdentity identity, AntiForgeryToken sessionToken, AntiForgeryToken fieldToken) 在 System.Web.Helpers.AntiXsrf.AntiForgeryWorker.Validate(HttpContextBase httpContext) 在 System.Web.Helpers.AntiForgery。 Validate() at System.Web.Mvc.ValidateAntiForgeryTokenAttribute.OnAuthorization(AuthorizationContext filterContext) at System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor) at System.Web.Mvc.Async.AsyncControllerActionInvoker. <>c__DisplayClass25。
The login method signature is-
登录方法签名是-
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
...
}
This is exactly the same as the signature for the method in a internet "ASP.NET MVC 4 Web Application" templated project, which indicates that Microsoft either felt the ValidateAntiForgeryToken was necessary/best practice, or simply added the attribute here because it was used everywhere else.
这与互联网“ASP.NET MVC 4 Web 应用程序”模板项目中方法的签名完全相同,这表明 Microsoft 要么认为 ValidateAntiForgeryToken 是必要的/最佳实践,要么只是在此处添加该属性,因为它被使用其他地方。
Obviously there is nothing I can do to handle the problem withinthis method as it isn't reached, the ValidateAntiForgeryToken is a pre-request filter and it is blocking the request before it reaches the controller.
显然,我无法处理此方法中的问题,因为它没有达到,ValidateAntiForgeryToken 是一个预请求过滤器,它在请求到达控制器之前阻止了请求。
I could check if the user is authenticated via Ajax before submitting the form and attempt to redirect to them if so, or simply remove the attribute.
我可以在提交表单之前检查用户是否通过 Ajax 进行了身份验证,如果是,则尝试重定向到他们,或者简单地删除该属性。
The question is this - I understand that the token is design to prevent requests from another site (CSRF) when the user is already authenticated against your siteso on that basis is it an issue to remove it from a form which by definition will be used by unauthenticated users?
问题是这样的 - 我知道令牌的设计目的是在用户已经针对您的站点进行身份验证时阻止来自另一个站点(CSRF)的请求,因此在此基础上从定义将使用的表单中删除它是一个问题未经身份验证的用户?
Presumably the attribute in this instance is designed to mitigate malicious actors providing fake login forms for your application (although by the time the exception is thrown presumably the user has already entered his or her details which will have been recorded - but it might alert them something is wrong). Otherwise submitting incorrect credentials to the form from an external site will result in exactly the same result as on the site itself surely? I am not relying on client validation/sanitation to clean up potentially unsafe input.
据推测,此实例中的属性旨在减轻为您的应用程序提供虚假登录表单的恶意行为者(尽管在抛出异常时,用户可能已经输入了他或她将被记录的详细信息 - 但它可能会提醒他们一些事情是错的)。否则,从外部站点向表单提交不正确的凭据肯定会导致与站点本身完全相同的结果?我不依赖客户端验证/清理来清理潜在的不安全输入。
Have other devs come across this issue (or do we have unusually creative users) and if so how have you resolved/mitigated it?
其他开发人员是否遇到过这个问题(或者我们是否有非常有创意的用户),如果有,你是如何解决/缓解它的?
Update:This issue still exists in MVC5, entirely intentionally, now with the error message "The provided anti-forgery token was meant for a different claims-based user than the current user." when using default template and Identity providers. There is a relevant question and interesting answer from Microsoft Developer Evangelist and Troy's fellow PluralSight author Adam Tuliper at Anti forgery token on login pagewhich recommends simply removing the token.
更新:这个问题在 MVC5 中仍然存在,完全是故意的,现在出现错误消息“提供的防伪令牌是为与当前用户不同的基于声明的用户而设计的。” 使用默认模板和身份提供程序时。Microsoft Developer Evangelist 和 Troy 的 PluralSight 同行作者 Adam Tuliper 在登录页面上的Antiforgery token 中有一个相关的问题和有趣的答案,建议简单地删除令牌。
采纳答案by Troy Hunt
I've just tested this pattern on ASafaWebwith the same result (it uses the same default implementation). Here's what I believe is happening
我刚刚在ASafaWeb上测试了这个模式,结果相同(它使用相同的默认实现)。这是我相信正在发生的事情
- Both login forms load with the same __RequestVerificationToken cookie (it's just the same one set in the same DOM) and the same __RequestVerificationToken hidden field. These tokens are keyed to an anonymous user.
- Login form A posts with the above tokens, validates them then returns an auth cookie which is now in the browser DOM
- Login form B posts alsowith the above tokens but now it's also posting with the auth cookie set from login form A as well.
- 两个登录表单都加载了相同的 __RequestVerificationToken cookie(它只是在同一个 DOM 中设置的同一个)和相同的 __RequestVerificationToken 隐藏字段。这些令牌以匿名用户为密钥。
- 登录表单 A 使用上述令牌发布,验证它们然后返回一个现在在浏览器 DOM 中的 auth cookie
- 登录表单 B也使用上述令牌发布,但现在它也使用登录表单 A 中设置的 auth cookie 发布。
The problem is that the token isn't validating in step 3 because it's keyed to an anonymous user but it's being passed in the request by an authenticated user. This is why you're seeing the error: The provided anti-forgery token was meant for user "", but the current user is "admin"
问题是令牌未在步骤 3 中进行验证,因为它是针对匿名用户的,但它是由经过身份验证的用户在请求中传递的。这就是您看到错误的原因:提供的防伪令牌适用于用户“”,但当前用户是“admin”
You're only having this issue because form B loaded before form A posted therefore form B is expecting to be posted by an anonymous user.
您遇到此问题只是因为表单 B 在表单 A 发布之前加载,因此表单 B 期望由匿名用户发布。
Is it an issue to remove it from a form which by definition will be used by unauthenticated users?
从定义上将被未经身份验证的用户使用的表单中删除它是否有问题?
The predominant underlying risk that anti-forgery tokens protect against is CSRF which usuallytakes advantage of authenticated users due to the fact that any requests their browser can be tricked into issuing will be automatically accompanied by an auth cookie hence the action will be performed on their behalf. This risk doesn't exist on the login form because usually the user isn't authenticated and the worst CSRF case here is that a login is forged and then fails; you're not exactly transferring money on the user's behalf!
防伪令牌保护的主要潜在风险是 CSRF,它通常利用经过身份验证的用户,因为他们的浏览器可以被欺骗发出的任何请求都将自动伴随 auth cookie,因此操作将在他们的代表。这种风险在登录表单上不存在,因为通常用户没有经过身份验证,这里最糟糕的 CSRF 情况是登录被伪造然后失败;您并没有完全代表用户转账!
There are other advantages to the anti-forgery token though: for example it prevents brute force attacks actually executing the method and hitting the DB. You need to decide if you're less worried about this and more worried about the scenario you're encountering in your question. Either that or you need to drop down into the request pipeline somewhere and take action if an auth cookie is already present in the request before the anti-forgery validation occurs.
不过,防伪令牌还有其他优点:例如,它可以防止实际执行该方法并击中 DB 的蛮力攻击。您需要决定是不是更担心这一点,而更担心您在问题中遇到的情况。或者您需要在某处进入请求管道并在防伪验证发生之前如果请求中已经存在身份验证 cookie 并采取行动。
Frankly though, I'm not sure I see the problem; to reproduce this issue the user has to have multiple login forms open at the same time and then try logging into each in succession - is this really going to happen enough to worry about? And when it does happen, does it really matter that the second login returns a custom error page (which of course you'd do in production)? IMHO, leave the default behaviour.
坦率地说,我不确定我是否看到了问题。要重现这个问题,用户必须同时打开多个登录表单,然后尝试依次登录每个表单 - 这真的会发生吗?当它发生时,第二次登录返回一个自定义错误页面真的重要吗(你当然会在生产中这样做)?恕我直言,保留默认行为。
回答by Zia Ul Mustafa
The validation code that runs against an AntiForgeryToken also checks your logged in user credentials haven't changed – these are also encrypted in the cookie. This means that if you logged in or out in a popup or another browser tab, your form submission will fail with the following exception:
针对 AntiForgeryToken 运行的验证代码还会检查您登录的用户凭据是否未更改——这些凭据也在 cookie 中加密。这意味着如果您在弹出窗口或其他浏览器选项卡中登录或退出,您的表单提交将失败,并出现以下异常:
System.Web.Mvc.HttpAntiForgeryException (0x80004005):
The provided anti-forgery token was meant for user "", but the current user is "SomeOne".
You can turn this off by putting AntiForgeryConfig.SuppressIdentityHeuristicChecks = true;in Application_Startmethod inside Global.asax file.
您可以通过设置AntiForgeryConfig.SuppressIdentityHeuristicChecks = true来关闭此功能;在Global.asax 文件中的Application_Start方法中。
When a AntiForgeryToken doesn't validate your website will throw an Exception of type System.Web.Mvc.HttpAntiForgeryException. You can make this a little easier by at least giving the user a more informative page targeted at these exceptions by catching the HttpAntiForgeryException.
当 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);
}
}
回答by kamranicus
Okay, please correct me if my assumption is incorrect.
好的,如果我的假设不正确,请纠正我。
When this exception gets thrown, you would prefer to let the user continue on while also maybe displaying a relevant message/warning? Because when this exception gets thrown, we can already know if the current user is authenticated--we have access to the request.
当抛出此异常时,您希望让用户继续操作,同时也可能显示相关消息/警告?因为当这个异常被抛出时,我们已经可以知道当前用户是否通过了身份验证——我们可以访问请求。
So, why wouldn't this be acceptable?
那么,为什么不能接受呢?
[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");
}
}
Am I missing something obvious here? I'll remove this answer if it has some implication I'm not seeing.
我在这里遗漏了一些明显的东西吗?如果这个答案有一些我没有看到的暗示,我会删除它。
回答by MVCdragon
I had the same issue, I solved it by adding machineKey to system.web>
我遇到了同样的问题,我通过将 machineKey 添加到 system.web> 解决了它
<machineKey validationKey="your machine key numbers"
decryptionKey="Your description key nuumbers" validation="SHA1" decryption="AES" />
Here's a site that generate unique Machnie Keys http://www.developerfusion.com/tools/generatemachinekey/
这是一个生成唯一 Machnie 密钥的站点 http://www.developerfusion.com/tools/generatemachinekey/
Works great, I understand that Anti Forgery may not be needed in a Login page, but if someone is determined to hack you site, it comforting to know that [ValidateAntiForgeryToken] is there to thwart them off.
效果很好,我知道在登录页面中可能不需要 Anti Forgery,但如果有人决心入侵您的网站,知道 [ValidateAntiForgeryToken] 可以阻止他们,这令人欣慰。

