We have a page with several forms. Each has its own @Html.AntiForgeryToken()
. On my local machine everything is great.
We deployed to Azure (PAAS), but the __RequestVerificationToken
is not being created on every request. Sometime it is there and sometime I get the The required anti-forgery cookie is not present and rarely I get the tokens do not match error.
I'm completely clueless at this point. I can't figure out if there's something wrong in our code or on Azure environment? No ajax in these forms.
We have added the <machineKey>
section to our web.config. No caching. Sometimes it occurs on new devices from the first time.
__RequestVerificationToken. www.grpgroup.co.uk. This is an anti-forgery cookie set by web applications built using ASP.NET MVC technologies. It is designed to stop unauthorised posting of content to a website, known as Cross-Site Request Forgery.
The automatic generation of antiforgery tokens for HTML form elements happens when the <form> tag contains the method="post" attribute and either of the following are true: The action attribute is empty ( action="" ). The action attribute isn't supplied ( <form method="post"> ).
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.
AntiForgeryToken() Generates a hidden form field (anti-forgery token) that is validated when the form is submitted.
After spending a significant amount of time with investigation, using a combination of Sentry and Azure web server logs, I've found 2 major causes of the mentioned errors:
1) On mobile phones, when the browser is in the background, it may be abruptly stopped by the OS to free up resources. When this happens, usually, the page is stored on the phone's drive, and reloaded from there once the browser is re-opened.
The problem, however, is that by this time, the Anti-Forgery Token, which is a session cookie, has already expired, since this is essentially a new session. So the page loads without an Anti-Forgery Cookie, using HTML from the previous session. This causes the The required anti-forgery cookie is not present
exception.
2) While seemingly related, the tokens do not match
exception is usually only tangentially related. The cause seems to be user behaviour of opening multiple tabs at the same time.
The Anti-Forgery Cookie is only assigned when a user arrives to a page with a form on it. This means that they can go to your homepage, and have no anti-forgery cookie. Then they can open multiple tabs using middle-click. The multiple tabs are multiple parallel requests, each of them without an anti-forgery cookie.
As these requests don't have an anti-forgery cookie, for each of them, ASP.NET generates a separate pseudo-random token for their cookie, and uses that in the form; however, only the result of the last header received will be retained. This means that all the other pages will have invalid tokens on the page, since their anti-forgery cookie was overridden.
For a solution, I've created a global filter that should ensure that
The code below is a FilterAttribute
that has to be added inside FilterConfig.cs
as a global filter. Please do note that, while I do not believe 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)
{
}
}
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