__RequestVerificationToken is not always being created
Asked Answered
D

3

8

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.

Dalury answered 11/10, 2017 at 8:34 Comment(5)
Have you ever found the answer for this?Nutshell
No, we had to remove the antiforgery check (it was used for simple integration forms. Nothing critical about it). Our Azure setup had two logical CDs, so my guess it is related to different CDs handling the request and response. That's just my guess.Dalury
I'm sad to hear that, though I don't think this should be a problem unless you have the domain defined in the form action as well (which would then redirect the user to another CD for the POST, but I doubt that would be the case since if you used the UrlHelper, that doesn't redirect users between domains).Nutshell
Note that You can use multiple forms with antifourgerytoken inside a page, second You can't send it through GET request, Third Yu can't use different salt values in your calls to Html.AntiForgeryToken(salt) and fourth using AJAX may require extra work to ensure the token is included in the POST, So I can't answer the question unless you share some of your code!Gurge
have you experienced it failing when you try submit the form or do you just have the failures audited somewhere?Identify
N
3

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)
    {
    }
}
Nutshell answered 30/11, 2017 at 19:50 Comment(0)
P
1

I believe your problem comes from having multiple forms with different anti-forgery tokens on one page. When page is requested you get two different tokens in forms hidden fields but only one token in the cookies. Form POST contains mismatching tokens that causes an error.

Try how AFT will work for you if page contains only one form. If it works OK then my assumption is correct.

This answer contains possible solution for page with multiple forms however it has some security drawbacks as explained here.

I'm not sure why everything works OK on your localhost. I've created simple application and tried form POST with correct cookies token but old Form token from previous session. To my surprise such POST successfully passes. May be asp.net has some special handling for local requests in this case. I haven't found any info on this.

If my answer still doesn't help you could you please provide following data for further analysis:

  1. Original page request with returned HTTP headers and form anti-forgery tokens.
  2. Form POST request with sent HTTP headers.
Pleistocene answered 5/11, 2017 at 7:5 Comment(2)
Based on the OP's post, the issue not a mismatch of tokens (btw if you have multiple tokens), and also doesn't seem like an issue that is always present. Also, ASP.NET decrypts the anti-forgery tokens before comparison. It doesn't matter if you have 2 different tokens in a form if they both contain data corresponding to the cookie. Additionally, I see it in my own system that multiple forms and form tokens aren't the issue, the problem is that there is no cookie sent by the browser at all.Nutshell
True, tokens aren't just compared as binary buffers, but are decrypted and compared by security token kept inside. Good article on anti-forgery tokens internals: codeproject.com/Articles/793384/…. OP mentioned that sometimes he get 'tokens do not match error' so my assumption still could be an issue. If not, then info I've requested at the bottom of my answer should clarify at least whether it's a problem of local client or remote server.Pleistocene
K
0

I had the same issue happening for me only after I pushed to server, it was good when it it was tested in localhost, after lot of debugging found out that there was more than one Antiforgery key created in the page, once I removed the extra one, page started loading up fine.

Kirschner answered 9/12, 2023 at 1:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.