6

I have a site that receives a large amount of the following errors:

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

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

I would like to prevent the site from throwing an error if the anti-forgery token is meant for no user but contains a user on the login page, for example:

The provided anti-forgery token was meant for user "", but the current user is "Garret".

I don't want this exception to apply to any other page other than the login page. So I don't want to add AntiForgeryConfig.SuppressIdentityHeuristicChecks = true; to the entire site. I also want to keep the site as secure as possible since it contains HIPAA data. What can I do to keep it as secure as possible but still try to prevent this error on the login page because it is making it difficult for users to use?

The site is hosted on load balancing servers but I don't think this is the issue. I think the error is mostly caused by using the browser's back button, having the login page opened for a while before logging in, already being logged in or pressing login more than once. Also some users access it through an application that may not be loading the page and just trying to post the login information.

So please let me know what the best option is to prevent this error on the login page while still being as secure as possible?

Community
  • 1
  • 1
Garrett Fogerlie
  • 4,450
  • 3
  • 37
  • 56

4 Answers4

4

What I ended up doing, and it seems to be working while still providing the same security, is to manually check the antiforgery token after checking if the user is logged in.

    [HttpPost]
    [AllowAnonymous]
    //[ValidateAntiForgeryToken]
    [OutputCache(NoStore = true, Duration = 0, VaryByParam = "None")]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        // Skip the login process if the user is already logged in
        if (User.Identity.IsAuthenticated) 
        {
            return RedirectToAction("Index", "Home");
        }
        // Check the anti forgery token now
        System.Web.Helpers.AntiForgery.Validate();
        ...
Garrett Fogerlie
  • 4,450
  • 3
  • 37
  • 56
  • It seems prudent to check whether the User currently authenticated matches the user you are trying to login with so that if users share a PC the user doesn't gain access to a orphaned session. – Kaizen Programmer Dec 18 '17 at 21:38
2

There is an answer for concurrent login attempts originated from certain user:

How can I fix anti-forgery token was meant for user "", but the current user is "xxxx " error

If AntiForgeryToken exception comes when the user has too long time to fill in login page, simply redirect to that page and show a message explaining his/her session (i.e. security token) had expired.

[HttpPost]
[ValidateAntiForgeryToken]
[HandleError(View="Login", ExceptionType = typeof(HttpAntiForgeryException))]
public ActionResult Login(LoginModel model)
{
     // some login processing stuff
}

If the exception comes from user's clicking back button, provide JS function on client-side which prevent the back button action on login page only:

<body onload="window.history.forward()">...</body>

// or wrap it inside a function
<script type="text/javascript">
function noBackOnLogin() {
        window.history.forward();
        history.pushState(null, document.title, url); // if you want to include Mobile Safari, see https://stackoverflow.com/a/34337617/6378815
    }
</script>
<body onload="noBackOnLogin();" onpageshow="if (event.persisted) noBackOnLogin();">...</body>

For another case that certain user has already logged in, redirect to the index or somewhat page indicating their user name (with User.Identity.Name).

I hope this explanation useful.

Community
  • 1
  • 1
Tetsuya Yamamoto
  • 24,297
  • 8
  • 39
  • 61
  • Thanks for the quick answer, I haven't had the chance to test it but when I do I'll report back and mark this accepted if it works. – Garrett Fogerlie May 30 '16 at 04:37
1

Instead of checking User.Identity.IsAuthenticated I used a custom attribute to handle the exceptions and redirect the user to the home page if it is a HttpAntiForgeryToken

I believe this avoids any potential security concerns of using the other methods

public override void OnException(ExceptionContext filterContext)
    {
        var controllerName = (string)filterContext.RouteData.Values["controller"];
        var actionName = (string)filterContext.RouteData.Values["action"];
        var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
        if (filterContext.Exception is HttpAntiForgeryException)
        {
            filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary
                {
                    { "action", "Index" },
                    { "controller", "Home" }
                });

            filterContext.ExceptionHandled = true;
        }
}
bla9x
  • 469
  • 7
  • 17
0

To answer @Michael_B's concern, how about you change this part of Garrett's solution:

    // Skip the login process if the user is already logged in
    if (User.Identity.IsAuthenticated) 
    {
        return RedirectToAction("Index", "Home");
    }
    // Check the anti forgery token now
    System.Web.Helpers.AntiForgery.Validate();
    ...

to this:

    // if not authenticated, check anti forgery token now:
    if (!User.Identity.IsAuthenticated) 
    {
        System.Web.Helpers.AntiForgery.Validate();
    }
    // run rest of login process normally

    ...
TechSavvySam
  • 1,382
  • 16
  • 28