Skip to main content

Anti-forgery token and anti-forgery cookie related issues

Anti-forgery token prevents CSRF (Cross-Site Request Forgery) attacks. The server associates this token with current user’s identity and sends it to the client. In the next request from client, the server expects to see this token. If the token is missing or it is different, then the server rejects the request (Reference).

I have recently worked on some anti-forgery related errors. These are the error messages I saw in Event Viewer:

The provided anti-forgery token was meant for a different claims-based user than the current user.

The provided anti-forgery token was meant for user “”, but the current user is “andre.boucher”.

The anti-forgery cookie token and form field token do not match.

The required anti-forgery cookie “__RequestVerificationToken” is not present.

A sample stack trace:

at System.Web.Helpers.AntiXsrf.TokenValidator.ValidateTokens(HttpContextBase httpContext, IIdentity identity, AntiForgeryToken sessionToken, AntiForgeryToken fieldToken)
at System.Web.Helpers.AntiXsrf.AntiForgeryWorker.Validate(HttpContextBase httpContext)
at System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass21.<BeginInvokeAction>b__19(AsyncCallback asyncCallback, Object asyncState)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeAction(ControllerContext controllerContext, String actionName, AsyncCallback callback, Object state)
at System.Web.Mvc.Controller.<BeginExecuteCore>b__1c(AsyncCallback asyncCallback, Object asyncState, ExecuteCoreState innerState)
Anti-forgery token and anti-forgery cookie related issues

Error messages are pretty clear about the root causes but the solution is not always straightforward. In this post, I will share some information about the root causes of these errors and possible solutions.

SameSite cookie may come handy about CSRF attacks as well. Have a look at this post for a related issue I have recently run into: How to turn off SameSite cookie attribute?

Root Cause

Long story short: For anti-forgery validation to pass, security token of the session token must be equal to the security token of the field token. If they don’t match, you will see the errors above in Event Viewer.

You may see different names for these cookies in different documents. They can be used interchangeably:

Session token = Cookie token
Field token = Form token
Security token = anti-XSRF Token

Longer story: For validation to pass, form token and session token are correlated (this is not comparison for equivalence. You can’t correlate them by simply comparing them in Fiddler). There is anti-XSRF token inside each of those. anti-XSRF token is the one that should match precisely.

How to reproduce and test the issue

This article mentions the way to decode these tokens and find the anti-XSRF token so you can compare for equivalence. For the example in he article:

  • Cookie/Session token:
    Aq81hoVCPIpq3Q6xjBi0EFKKwSFwnKROgS7tyXF393eAN8rdMNZwkVkEgjQokKviKLVST1iWdgDxBt-g3FIughAsczUO7tyWhtz3fs88xMM1
    After decoding: 01-1A-CF-C9-ED-F1-3E-1E-7D-C9-9E-BE-90-2E-22-91-36-01
  • Form token:
    i411mJIr0mZKrk17g4Hf-0_G6aXOJLkzzGfd5yn2mVsTqj-35j_n0YUUCzFRXoFet3BXUVpBicpL3p-AqPPA3XEXEtykt4X-_MbRIxLQH6M1
    After decoding: 01-1A-CF-C9-ED-F1-3E-1E-7D-C9-9E-BE-90-2E-22-91-36-00-00-00-00

As you see, the raw tokens are different but extracted anti-XSRF tokens are the same so the validation passes. (There are additional bytes at the end. Those are flags. If it was an authenticated user, the username was going to be encoded there).

Microsoft documentation says the same. Session and form tokens are not compared. The anti-XSRF tokens inside of them is compared and it should be identical for validation to pass.

To validate the incoming anti-XSRF tokens, the developer includes a ValidateAntiForgeryToken attribute on her MVC action or controller, or she calls @AntiForgery.Validate() from her Razor page. The runtime will perform the following steps:

1- The incoming session token and field token are read and the anti-XSRF token extracted from each. The anti-XSRF tokens must be identical per step (2) in the generation routine.

2- If the current user is authenticated, her username is compared with the username stored in the field token. The usernames must match.

3- If an IAntiForgeryAdditionalDataProvider is configured, the runtime calls its ValidateAdditionalData method. The method must return the Boolean value true.

If validation succeeds, the request is allowed to proceed. If validation fails, the framework will throw an HttpAntiForgeryException.

Solution for anti-forgery token and cookie issues

A possible solution to anti-forgery related errors depends on the way how the issue occurs. Try to find out the user behavior that causes these errors.

User behavior

In my case, we found out that users were using the application in a way that is not supposed to be used. One of the scenarios we came across:

  1. User goes to the login page
  2. User opens a second tab in the same browser and goes to the login page
  3. User logins in the first tab (or the  second, the order doesn’t matter)
  4. User attempts to login in the remaining login tab

Similarly, using the Back button to go back to the login page or not using Log Out button to exit the application may cause these anti-forgery issues.

Informing your users about these points can significantly reduce the number of anti-forgery errors thrown:

  • Using the application only in one tab
  • Not using the Back button to go back to the login page
  • Using “Log out” button once they are done with the application
  • There might be a scenario I may be missing here. I would recommend contacting your users and asking if they remember what action they took to generate the issue

Catch the Exception

Catching the anti-forgery exceptions in your code and redirecting user to the homepage if they are already authenticated should prevent most of the errors. A sample code block is below.

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
...
}
 
protected override void OnException(ExceptionContext filterContext)
{
    base.OnException(filterContext);
 
    var action = filterContext.RequestContext.RouteData.Values["action"] as string;
    var controller = filterContext.RequestContext.RouteData.Values["controller"] as string;
 
    if ((filterContext.Exception is HttpAntiForgeryException) &&
        action == "Login" &&
        controller == "MyController" &&
        filterContext.RequestContext.HttpContext.User != null &&
        filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated)
    {
        filterContext.ExceptionHandled = true;
 
        // redirect/show error/whatever?
        filterContext.Result = new RedirectResult("/homepage");
    }
}

Implementing this code block along with informing your users about best practices should be an efficient workaround. Please note that it may take more time to fine tune this code block. There is no one solution that fits all.

Remove the anti-forgery validation from the login page

Internet is split between about using the anti-forgery validation in the public pages (no authentication required) such as login, about, register pages. Anti-forgery token’s main purpose is preventing attacker to use authentication cookie for doing “stuff” on behalf of the actual user. Since the user hasn’t authentication yet in the login page, I don’t see a significant case to use anti-forgery validation (and struggle with related errors!).

The post below explains this in more detail:

The predominant underlying risk that anti-forgery tokens protect against is CSRF which usually takes advantage of authenticated users due to the fact that any requests their browser can be tricked into issuing will be automatically accompanied by an auth cookie hence the action will be performed on their behalf. This risk doesn’t exist on the login form because usually the user isn’t authenticated and the worst CSRF case here is that a login is forged and then fails; you’re not exactly transferring money on the user’s behalf!

Source

There are several other discussions around this topic. Please have a look at them to find out different perspectives so that you can make the best decision in your scenario.

Try quick fixes

The most common “possible solutions” to anti-forgery token/cookie related issues are disabling output caching and enabling heuristic checks. I will include the code snippets here. For details, please have a look at the related post.

Disable output caching:

[OutputCache(NoStore = true, Location = System.Web.UI.OutputCacheLocation.None)]

Add “heuristic checks” to the Application_Start method of Global.asax file:

AntiForgeryConfig.SuppressIdentityHeuristicChecks = true;

For for more details about CSRF attacks and prevention techniques, check this post out: How to protect your ASP.NET WebForms application against CSRF (Cross-Site Request Forgery) attacks?

Leave a Reply

Your email address will not be published. Required fields are marked *