Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

__RequestVerificationToken is not always being created

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.

like image 890
TamerM Avatar asked Oct 11 '17 08:10

TamerM


People also ask

What is __ Requestverificationtoken?

__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.

How is Antiforgery token generated?

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"> ).

Is anti-forgery cookie?

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.

What does HTML AntiForgeryToken () do?

AntiForgeryToken() Generates a hidden form field (anti-forgery token) that is validated when the form is submitted.


1 Answers

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

  1. The Anti-Forgery cookie is assigned on any page, even if the page has no form, and
  2. The Anti-Forgery cookie is not session-bound. It's lifetime should be adjusted to match the user login token, but it should persist between sessions in case a mobile device reloads the page without the session.

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)
    {
    }
}
like image 193
Kanadaj Avatar answered Oct 04 '22 05:10

Kanadaj