After spending a considerable amount of time with the investigation, using a combination of the Sentry and Azure web server logs, I found two main causes of the errors mentioned:
1) On mobile phones, when the browser is in the background, it may suddenly stop the OS to free up resources. When this happens, the page is usually stored on the telephone disk and reloaded from there when the browser reopens.
However, the problem is that by this point, the Anti-Forgery token, which is the session cookie, has already expired since it is essentially a new session. Thus, the page loads without the Anti-Forgery Cookie, using the HTML from the previous session. This throws the exception The required anti-forgery cookie is not present .
2) Apparently, the exception tokens do not match usually only related in relation. It seems that the reason is the user behavior when opening multiple tabs at the same time.
Anti-Forgery Cookie is assigned only when the user comes to a page with a form on it. This means that they may go to your homepage and not have an anti-fake cookie. Then they can open several tabs using the middle click. Multiple tabs are multiple concurrent requests, each of which does not contain an anti-fake cookie.
Since these requests do not have an anti-virus cookie, for each of them ASP.NET generates a separate pseudo-random token for its cookie and uses it in the form; however, only the result of the last header received will be saved. This means that all other pages will display invalid tokens on the page, as their anti-fake cookie has been canceled.
For the solution, I created a global filter, which should ensure that
- The Anti-Forgery cookie is set on any page, even if the page does not have a form, and
- The Anti-Forgery cookie is not associated with the session. This lifetime should be adjusted according to the user's login token, but it must be maintained between sessions if the mobile device reloads the page without a session.
Below is the FilterAttribute code that should be added inside FilterConfig.cs as a global filter. Please note that although I do not believe that this would create a security hole, I am by no means a security expert, so any input is welcome.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class AntiForgeryFilter : FilterAttribute, IActionFilter { public void OnActionExecuting(ActionExecutingContext filterContext) { var cookie = filterContext.HttpContext.Request.Cookies.Get(AntiForgeryConfig.CookieName); var addCookie = true; if (string.IsNullOrEmpty(cookie?.Value)) { cookie = filterContext.HttpContext.Response.Cookies.Get(AntiForgeryConfig.CookieName); addCookie = false; } if (string.IsNullOrEmpty(cookie?.Value)) { AntiForgery.GetTokens(null, out string cookieToken, out string _); cookie = new HttpCookie(AntiForgeryConfig.CookieName, cookieToken) { HttpOnly = true, Secure = AntiForgeryConfig.RequireSsl }; } cookie.Expires = DateTime.UtcNow.AddYears(1); if(addCookie) filterContext.HttpContext.Response.Cookies.Add(cookie); } public void OnActionExecuted(ActionExecutedContext filterContext) { } }