Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RequestVerificationToken does not match

I have a problem with the anti CRSF MVC mechanism. The cookie and the form input returned does not match. I'm getting an error every single time, only in one specific page. In the rest of the application it works well.

The server is returning HTTP 500 Internal Server Error and I can see on the log this exception:

[System.Web.Mvc.HttpAntiForgeryException]: {"A required anti-forgery token was not supplied or was invalid."}

This is the hidden input that the server is generating is:

<input name="__RequestVerificationToken" type="hidden" value="QK8P7rjyZE6Vm5seY7Fr704YCOoFGdTIMzl1W7R0ZFpXSMjGKLG2T05DfFSYTxvtQCEx7DDT69DGsDB2+ZXFHY8oAjiKz0gw8BhDFywgmfIpoXnGpj7fONNzIIfvbrDrE9WJsMu6Io/0bDLM5WfKs0zktiNjyOWpfYrmnfINYmjW8NLOZFoz74xTcgTptAld"> 

And this is the Cookie returned:

Set-Cookie:__RequestVerificationToken_L2VGbG93=skmTAVI8HCbfxDS+xhioIMIISL3UOBI7qJM1JbHjTtAqKl4W70pDUcTKMm0p3R3mrHDziE8vXw0C0OO4HArzWO1/e6py+v/cFdbe9maFgjl4jMiZ9Wc4YIhC6+IUXkk6yqJDJ8dCIr8qtGaYcD9IX+m7/SlVhu521KQSWJYRcaY=; path=/; HttpOnly 

When I examine what the server is sending, the cookie is exactly the same, but the payload has different encoding I think:

__RequestVerificationToken:QK8P7rjyZE6Vm5seY7Fr704YCOoFGdTIMzl1W7R0ZFpXSMjGKLG2T05DfFSYTxvtQCEx7DDT69DGsDB2%2BZXFHY8oAjiKz0gw8BhDFywgmfIpoXnGpj7fONNzIIfvbrDrE9WJsMu6Io%2F0bDLM5WfKs0zktiNjyOWpfYrmnfINYmjW8NLOZFoz74xTcgTptAld 

The differences are in two characters that appear encoded:

    /    ->   %2F       +    ->   %2B 

Those are the only differences I can find between the hidden input field, and the post payload.

What could be the problem that is causing that ValidateAntiForgeryToken fails in verify the token?

Regards.

like image 201
vtortola Avatar asked Oct 14 '11 11:10

vtortola


People also ask

What is __ Requestverificationtoken?

The form tag helper injects a hidden form field named __RequestVerificationToken at the end of every form with an encrypted value representing the token. This value is also sent to the browser in a samesite cookie.

What is http anti-forgery exception?

Anti-forgery token is used to prevent CSRF (Cross-Site Request Forgery) attacks. Here is how it works in high-level: IIS server associates this token with current user's identity before sending it to the client. In the next client request, the server expects to see this token.

What is anti-forgery cookie?

Cross-site request forgery (also known as XSRF or CSRF) is an common attack against web apps that store authentication tokens in the cookies. Browser will automatically attach these authentication cookies with every request to the website.

How do I refresh AntiForgeryToken?

You can achieve this by simply returning the AntiForgeryToken after they log in. No need to re-use the same token 2 times. You can use JS similar to this to load ONLY the new AntiForgeryToken value into the comment form.


2 Answers

I've had and resolved several issues with ValidateAntiForgeryToken lately, so I'll share my findings with you.

Salt: Since you mention this only happens on a single page, my best guess is that you are using different salt values in your calls to Html.AntiForgeryToken(salt) and ValidateAntiForgeryToken(salt) calls.

AJAX: as another answer has said, using AJAX may require extra work to ensure the token is included in the POST. Here is my favorite simple, automatic solution to add the token to all AJAX POST requests.
In your question though, you state that you have verified that the token is sending. Have you verified that you're only sending the token once? I found out that an AJAX call of mine was sending the token twice, which combined the values, and caused it to fail.

Machine Key and Cookies: this issue is ugly, easy to spot (causes exceptions), but not very intuitive. The validation cookies and tokens are encoded and decoded using a unique "machine key". This means that if you have a server farm, or change your server, your cookie will no longer be valid. Closing your browser fixes the issue (because the cookie is a session cookie). However, some people leave their browser windows open in the background for a long time!
The solution is to set a "machine key" in your config file. This will tell MVC to use the same key on all servers, ensuring that the cookie will be decryptable everywhere.

Encoding Bugs: using a testing utility called jMeter, we attempted to load-test our pages, only to find out that it had a bug that caused our token to have 2 extra " around the value.
The solution is to lower your trust in your tools! Test in a browser, and if that works, create a test that extracts the token and cookie values, and set a breakpoint to verify the results.

If none of these things work for you, then I'd recommend taking a look at the MVC source code for ValidateAntiForgeryTokenAttribute, specifically the OnAuthorization method. It will help you see the different steps where validation could fail. You might even inspect your error's Exception.StackTrace to determine which part is failing.

As a side note, I really dislike the implementation of ValidateAntiForgeryToken in MVC, because:

  • There are about 5 verification steps that can fail, but there is only one generic error message.
  • The class is sealed, so it cannot be extended with additional functionality.
  • The encryption method is weird - it initializes a Page and creates an artificial ViewState to encrypt the tokens and cookies. Seems overkill.

So, I grabbed the source code, and created my own specialized subclass, which also turned out to be very helpful in debugging its issues, because I could set breakpoints on the validation methods, and it was really easy to determine which validation step was failing.

like image 156
7 revs Avatar answered Sep 20 '22 11:09

7 revs


If this is being sent as an Ajax request, then the current setup of the framework isn't build to do this naturally.

Luckly Phil Haak wrote a nice blog post on dealing with CSRF and Ajax -> Preventing CSRF With Ajax which goes into some good detail about how to use the existing framework and modify it to work for Ajax/Json.

like image 38
Stephen Avatar answered Sep 18 '22 11:09

Stephen