I have been going through the error logs of a development project and found the following error (name changed to protect the guilty innocent)-
The provided anti-forgery token was meant for user "", but the current user is "admin".
This was not an especially difficult issue to reproduce-
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)
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.
Obviously there is nothing I can do to handle the problem within this method as it isn't reached, the ValidateAntiForgeryToken is a pre-request filter and it is blocking the request before it reaches the controller.
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.
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 site so on that basis is it an issue to remove it from a form which by definition will be used by unauthenticated users?
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 page which recommends simply removing the token.
The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.
The feature doesn't prevent any other type of data forgery or tampering based attacks. To use it, decorate the action method or controller with the ValidateAntiForgeryToken attribute and place a call to @Html. AntiForgeryToken() in the forms posting to the method.
Anti-Forgery TokensOne token is sent as a cookie. The other is placed in a hidden form field. The tokens are generated randomly so that an adversary cannot guess the values. When the client submits the form, it must send both tokens back to the server.
Anti-forgery token's main purpose is to prevent attacker using authentication cookie for doing things on behalf of the actual user. Since the user isn't authenticated yet in the login page, there are customers removing the validation.
I've just tested this pattern on ASafaWeb with the same result (it uses the same default implementation). Here's what I believe is happening
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"
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.
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 usually takes 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!
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.
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.
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:
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_Start method inside Global.asax file.
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.
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); } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With