Apparently Random Error: "Antiforgery token validation failed. The antiforgery cookie token and request token do not match."

See original GitHub issue

Background

I have a relatively new ASP.NET Core 2 site. It’s running on just one server (Windows Server 2012 R2, IIS 8.5), and I only restart the site once every few days when I upload an update. About once a day, a user’s request fails due to rejection by the anti-forgery system. These are POST requests, and there’s nothing particularly special about them. I’m including the anti-forgery value in the POST request, and 99% of the time, POST requests work. But when they don’t, the stdout log says, “Antiforgery token validation failed. The antiforgery cookie token and request token do not match.” I’ve already posted this question on Stack Overflow and the ASP.NET Core forums, and I haven’t gotten any useful answers.

Errors

I’ve included the relevant portions of the stdout log below.

info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 POST [domain redacted] application/x-www-form-urlencoded 234
info: Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ValidateAntiforgeryTokenAuthorizationFilter[1]
      Antiforgery token validation failed. The antiforgery cookie token and request token do not match.
Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException: The antiforgery cookie token and request token do not match.
   at Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgery.ValidateTokens(HttpContext httpContext, AntiforgeryTokenSet antiforgeryTokenSet)
   at Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgery.<ValidateRequestAsync>d__9.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ValidateAntiforgeryTokenAuthorizationFilter.<OnAuthorizationAsync>d__3.MoveNext()
info: Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker[3]
      Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.AutoValidateAntiforgeryTokenAuthorizationFilter'.
info: Microsoft.AspNetCore.Mvc.StatusCodeResult[1]
      Executing HttpStatusCodeResult, setting HTTP status code 400
info: Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker[2]
      Executed action /Index in 2.6224ms
warn: Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgery[1]
      Antiforgery validation failed with message 'The antiforgery cookie token and request token do not match.'.

For requests that result in the above stdout output, IAntiforgery.IsRequestValidAsync agrees by returning false. Notice the error message “The antiforgery cookie token and request token do not match.” Here’s a reduced example of a failed POST request and the associated cookie.

POST: __RequestVerificationToken= CfDJ8F9Fs4CqDFpLttT96eZw9WHjWfHO8Yawn35k4Yq3gDK5n1TDJDDiY5o86VQs1_qOVIYBydCizBU4knb7Jmq1-heGhwnMu2KmhUIiAd0xI7Sudv3GX-J0OI6wRfiPL4L1KRs2Pml8dbsDfwemewBqi18

Cookie: .AspNetCore.Antiforgery.ClRyCRmWApY=CfDJ8F9Fs4CqDFpLttT96eZw9WFtJht41WcNrmgshi2pFGwcxhr0_0hvINQc7Yl9Cbjhv-TiSNXeEctyKborLI49AcjHfWIgOmmKkbjOe7QMn8Z0WZtkQy5JcaBHKEGTu1p-La8JL8pZZqZy02Hrswpkh3I

I’ve also captured this data a few times after the request has failed with a 400 error (using some error handling middleware):

AntiforgeryTokenSet tokens = antiforgery.GetTokens(context);
tokens.CookieToken:  null
tokens.FormFieldName:  "__RequestVerificationToken"
tokens.HeaderName:  "RequestVerificationToken"
tokens.RequestToken:  "CfDJ8F9Fs4CqDFpLttT96eZw9WH33jSw5mM8h7RpEd3vGISQTRkx1rfwm-L2lfkvXKMBc-riESmoTo_fnIjeBbRmOo5KuJHr09f8B75sQ9g_djIVeeaGwMw5KE6W1O2-7Vi03fCnwlTv8l-BWGst76Ln-ZQ"

So here are the three strings:

POST String:  "CfDJ8F9Fs4CqDFpLttT96eZw9WHjWfHO8Yawn35k4Yq3gDK5n1TDJDDiY5o86VQs1_qOVIYBydCizBU4knb7Jmq1-heGhwnMu2KmhUIiAd0xI7Sudv3GX-J0OI6wRfiPL4L1KRs2Pml8dbsDfwemewBqi18"
Cookie String:  "CfDJ8F9Fs4CqDFpLttT96eZw9WFtJht41WcNrmgshi2pFGwcxhr0_0hvINQc7Yl9Cbjhv-TiSNXeEctyKborLI49AcjHfWIgOmmKkbjOe7QMn8Z0WZtkQy5JcaBHKEGTu1p-La8JL8pZZqZy02Hrswpkh3I"
antiforgery.GetTokens(context).RequestToken:  "CfDJ8F9Fs4CqDFpLttT96eZw9WH33jSw5mM8h7RpEd3vGISQTRkx1rfwm-L2lfkvXKMBc-riESmoTo_fnIjeBbRmOo5KuJHr09f8B75sQ9g_djIVeeaGwMw5KE6W1O2-7Vi03fCnwlTv8l-BWGst76Ln-ZQ"

The POST string and cookie string don’t match, but in my experience, even with requests ASP.NET Core considers legitimate, they never do. But strangely, the POST string and tokens.RequestToken don’t match either. I would think they should match, although I captured tokens.RequestTooken later in the request lifecycle, so maybe that has something to do with it.

ASP.NET Core 2 on GitHub

I decided to look at the source code of ASP.NET Core 2. I found this file, especially line 145:

https://github.com/aspnet/Antiforgery/blob/dev/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgeryTokenGenerator.cs

That line gets the message “The antiforgery cookie token and request token do not match.” from this file at line 134:

https://github.com/aspnet/Antiforgery/blob/dev/src/Microsoft.AspNetCore.Antiforgery/Resources.resx

So I think that’s where the message is originating, but I’m still left wondering why this is happening.

Question

Would someone please help me figure out why these anti-forgery tokens aren’t validating? Is it possible the user’s Web browser is mangling the cookie or POST data? Does anyone have experience in this area or any suggestions? Thank you.

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Reactions:3
  • Comments:51 (18 by maintainers)

github_iconTop GitHub Comments

5reactions
TheCloudlessSkycommented, Feb 22, 2019

We’ve been experiencing this problem for several years in a production application on ASP.NET MVC 5 (though similar anti-forgery mechanism to ASP.NET Core). Our application has a few components that are mini-single-page-applications. So, there’s a lot of requests via JavaScript that we do that include anti-forgery protection. We’ve been able to prevent anti-forgery errors by introducing a couple of changes:

  1. Disable HTTP caching on HTML views that are returned to the browser - If the page was cached, the user could hit back and be served a page with a state form token that no longer matches the cookie. Our application is behind authentication and we do caching at many other levels (HTML fragments/views, data layers, etc) so full HTTP caching of a page doesn’t make sense for us anyways.
  2. Detect logging in/out of multiple tabs and clearing cookies in the same browser session - If a user had a stale anti-forger token on a page but logged out with a separate tab, cleared their cookies, and logged back in with another tab, they had the possibility of using that old tab with the stale anti-forgery token. We prevented this from happening by allowing the tabs to communicate when loaded (e.g. via localStorage session events or using the Broadcast Channel API) and show a message on the “stale” tab. A lot of sites do this similar pattern (e.g. GitHub, AWS Console, etc). This reduced a large majority of our problems.
  3. Prevent form submission double-clicking - This helped anti-forgery errors caused by double-form submissions on pages like our login if the anti-forgery token happened to be re-generated once logged in.

Although this fixed most of our problems, we still have had consistent anti-forgery problems from some of our users. We have a heartbeat-like request that sends useful information for aggregating/reporting later on in the user interface. This heartbeat-like request would continuously fail for certain users meaning that their data was lost and couldn’t be reported on. Thankfully, the anti-forgery error was transparent to them but it meant a discrepancy in data for reporting.

In a recent investigation back into this problem, I found some interesting revelation in our logs:

  1. When the specific users were having problems some of their heartbeat-like requests were being accepted and validated by our servers but some were failing with anti-forgery mismatch exceptions.
  2. When the users that experienced anti-forgery problems first started work in the day, our logs would show concurrent requests from them with the referrer empty.

Here’s what this is caused by:

  1. The user logs into the application with an authentication cookie.
  2. The user changes their browser setting to Open a specific page or set of pages on startup (e.g. in Chrome).
  3. They set multiple pages of our application in that list (e.g. /apples and /bananas).
  4. They’re done for the day and close their browser.
  5. The __RequestVerificationToken cookie is cleared because it’s only alive for as long as the browser’s session.
  6. The .ASPXAUTH cookie is not cleared because it has a default expiration of 1 year.
  7. The next time the user launches their browser, /apples and /bananas are launched concurrently in our app. This could also be accomplished just by doing something like middle-clicking on a tab more than once, but the pattern seems to fit since this causes the referrer to be empty. Both of those pages render a form with the anti-forgery token (e.g. @Html.AntiForgeryToken()).
  8. Both requests generate a new __RequestVerificationToken cookie and send it as part of the response because the request does not have a __RequestVerificationToken. When the form’s token is generated during the request, it’s for the unique cookie token per-request.
  9. The browser chooses the last cookie to be saved, thus causing the first tab’s form token to be invalidated because it no longer matches the cookie when the tab was loaded. When our heartbeat-like requests are sent from the “bad” tab, anti-forgery exceptions are thrown server-side.

I have some ideas to work around this, though I haven’t fully thought them through:

  1. Seed the cookie token based on the user’s current authentication context (e.g. in our case their internal user session id in our database). Anti-forgery (at least in MVC 5) doesn’t have a way to hook into this.
  2. Change the anti-forgery token to be the length of the user’s authentication session when it’s first created. This would prevent it from being cleared in the first place. I’m not a security expert, so I’m not sure if this is a good idea.
  3. Somehow detect the form anti-forgery token changes per-tab like we do with stale sessions. Obviously, the form tokens are always unique per-form… so I’m not sure how detecting a “change” could help here.
  4. Use a mechanism outside of our web servers (e.g. Redis) to prevent generating the anti-forgery token more than once. This seems expensive because there’s no way to hook into when ASP.NET generates a new anti-forgery token.

Does anyone else have any ideas on how we could solve this problem? @wessleym I’m not entirely sure if any of this can help solve your problem but I hope it may be able to provide some insight.

3reactions
CollapsedMetalcommented, Jan 23, 2019

If it’s worth something, iv’e been facing this issue since ASP.NET MVC 5. As it is described it happens 1 on 1000 times apparently random. No matter how hard I try, I still cannot reproduce this behaviour

Read more comments on GitHub >

github_iconTop Results From Across the Web

c# - Apparently Random Error: "Antiforgery token validation ...
But when they don't, the stdout log says, "Antiforgery token validation failed. The antiforgery cookie token and request token do not match.
Read more >
Antiforgery token validation failed. The antiforgery cookie ...
The logged error message is: Antiforgery token validation failed. The antiforgery cookie token and request token do not match. I can only ...
Read more >
Anti-forgery token and anti-forgery cookie related issues
Anti-forgery token is used to prevent CSRF (Cross-Site Request Forgery) ... The anti-forgery cookie token and form field token do not match.
Read more >
Apparently Random Error: "Antiforgery token validation failed ...
Coding example for the question Apparently Random Error: "Antiforgery token validation failed. The antiforgery cookie token and request token do not match.
Read more >
Antiforgery token validation failed when invoking negotiate ...
I have already tried passing the RequestVerificationToken and the cookies to signalR connection. But it is giving the same error. ".AspNetCore.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found