Archive

The MVC helper “Html.AntiForgeryToken()” can be used to protect your application against cross-site request forgery (CSRF). This will generate both a hidden field and a cookie that contains matching values that are validated on the server.

Our website utilizes multiple forms on the same view and each form contains an anti-forgery token. However, each call to “Html.AntiForgeryToken()” generates a different value. For example, multiple calls such as:

Looking back at the “GetFormInputElement” method, we can see the code checks for the existence of a token from the cookies collection using the method “GetCookieTokenNoThrow”. Drilling into the code shows it’s not complicated. If it exists, it deserializes into an “AntiForgeryToken”, otherwise it returns null.

So from what we can see, if a token can be deserialized from the request’s cookie collection, it’ll reuse that token instead of generating a new one. If a token doesn’t exist in the cookie collection, it’ll instantiate a new instance of “AntiForgeryToken” and randomly generate a new 16 byte array to represent the token.

Going back to the method “GetFormInputElement”, we can see it calls the method “SaveCookieToken” after generating or reusing the existing token.

After generating the first token and saving it to the cookie collection, all subsequent calls to the helper method “Html.AntiForgeryToken()” will follow the same steps and reuse the existing token from the cookie collection instead of generating a new value. Since it is a session cookie, this means the anti-forgery token’s value is generated only once during a browser session and is reused for all subsequent calls.

So why are the hidden field values different from one another if they are reusing the same token? To answer that, we have to look at the token serializer.

The “Protect” method uses the internal class “AspNetCryptoServiceProvider” to encrypt the token using our specified machineKey in the config. So while the encrypted values may look different, the decrypted values are the same. To test this, we can use the decompiled code from dotPeek to “Unprotect” the encrypted values.

In summary, the verification tokens generated from “Html.AntiForgeryToken()” are all identical within a browser session, regardless how many times we call it. The values appear different because they’re encrypted using our machineKey.