Site Meter

Prevent Cross-Site Request Forgery (CSRF) using ASP.NET MVC’s AntiForgeryToken() helper

Update: Since the Release Candidate of ASP.NET MVC, these anti-forgery helpers have been promoted to be included in the core ASP.NET MVC package (and not in the Futures assembly).

Cross-site scripting (XSS) is widely regarded as the number one security issue on the web. But since XSS gets all the limelight, few developers pay much attention to another form of attack that’s equally destructive and potentially far easier to exploit. Your application can be vulnerable to cross-site request forgery (CSRF) attacks not because you the developer did something wrong (as in, failing to encode outputs leads to XSS), but simply because of how the whole Web is designed to work. Scary!

How CSRF works

So, what’s it all about? All web application platforms are potentially vulnerable to CSRF, but in this post I’ll focus on ASP.NET MVC. Imagine you have a controller class as follows:

public class UserProfileController : Controller
    public ViewResult Edit() { return View(); }
    public ViewResult SubmitUpdate()
        // Get the user's existing profile data (implementation omitted)
        ProfileData profile = GetLoggedInUserProfile();
        // Update the user object
        profile.EmailAddress = Request.Form["email"];
        profile.FavoriteHobby = Request.Form["hobby"];
        ViewData["message"] = "Your profile was updated.";
        return View();

This is all very normal. First, the visitor goes to Edit(), which renders some form to let them change their user profile details. Secondly, they post that form to SubmitUpdate(), which saves the changes to their profile record in the database. There’s no XSS vulnerability here. Everything’s fine, right? We implement this sort of thing all the time…

Unfortunately, this innocent controller is an easy target for CSRF. Imagine that an attacker sets up the following HTML page and hosts it on some server of their own:

<body onload="document.getElementById('fm1').submit()">
    <form id="fm1" action="http://yoursite/UserProfile/SubmitUpdate" method="post">
        <input name="email" value="hacker@somewhere.evil" />
        <input name="hobby" value="Defacing websites" />

Next, they somehow persuade a victim to visit this page (basic social engineering, look it up). When this HTML page loads, it submits a valid form post to /UserProfile/SubmitUpdate on your server.

Assuming you’re using Windows authentication or some kind of cookie-based authentication system such as Forms Authentication, the automated form post will be processed within the victim’s established authentication context, and will successfully update the victim’s email address to something under the attacker’s control. All the attacker has to do now is use your “forgotten password” facility, and they’re taken control of the victim’s account.

Of course, instead of changing an victim’s email address, they can perform any action that the victim can perform with a single POST request. For example, they might be able to grant administrative permissions to another account, or post something defamatory to a CMS.

Ways to stop CSRF

There are two main ways to block CSRF:

  • Check that incoming requests have a Referer header referencing your domain. This will stop requests unwittingly submitted from a third-party domain. However, some people disable their browser’s Referer header for privacy reasons, and attackers can sometimes spoof that header if the victim has certain versions of Adobe Flash installed. This is a weak solution.
  • Put a user-specific token as a hidden field in legitimate forms, and check that the right value was submitted. If, for example, this token is the user’s password, then a third-party can’t forge a valid form post, because they don’t know each user’s password. However, don’t expose the user’s password this way: Instead, it’s better to use some random value (such as a GUID) which you’ve stored in the visitor’s Session collection or into a Cookie.

Using the AntiForgeryToken helpers

With Preview 5, Microsoft has added a set of helpers to the “futures” assembly, Microsoft.Web.Mvc.dll,The core ASP.NET MVC package includes a set of helpers that give you a means to detect and block CSRF using the “user-specific tokens” technique.

To use these helpers to protect a particular form, put an Html.AntiForgeryToken() into the form, e.g.,

<% using(Html.Form("UserProfile", "SubmitUpdate")) { %>
    <%= Html.AntiForgeryToken() %>
    <!-- rest of form goes here -->
<% } %>

This will output something like the following:

<form action="/UserProfile/SubmitUpdate" method="post">
    <input name="__RequestVerificationToken" type="hidden" value="saTFWpkKN0BYazFtN6c4YbZAmsEwG0srqlUqqloi/fVgeV2ciIFVmelvzwRZpArs" />
    <!-- rest of form goes here -->

At the same time, Html.AntiForgeryToken() will give the visitor a cookie called __RequestVerificationToken, with the same value as the random hidden value shown above.

Next, to validate an incoming form post, add the [ValidateAntiForgeryToken] filter to your target action method. For example,

public ViewResult SubmitUpdate()
    // ... etc

This is an authorization filter that checks that:

  • The incoming request has a cookie called __RequestVerificationToken
  • The incoming request has a Request.Form entry called __RequestVerificationToken
  • These cookie and Request.Form values match

Assuming all is well, the request goes through as normal. But if not, boom!, there’s an authorization failure with message “A required anti-forgery token was not supplied or was invalid”.

This prevents CSRF because even if a potential victim has an __RequestVerificationToken cookie, an attacker can’t find out its value, so they can’t forge a valid form post with the same value in Request.Form. But legitimate users aren’t inconvenienced at all; the mechanism is totally silent.

Using salt

Salt? What? In case you want to protect multiple forms in your application independently of each other, you can use a “salt” value when you call Html.AntiForgeryToken(), e.g.,

<%= Html.AntiForgeryToken("someArbitraryString") %>

… and also in [ValidateAntiForgeryToken], e.g.,

public ViewResult SubmitUpdate()
    // ... etc

Salt is just an arbitrary string. A different salt value means a different anti-forgery token will be generated. This means that even if an attacker manages to get hold of a valid token somehow, they can’t reuse it in other parts of the application where a different salt value is required. (If anyone can suggest other use cases for salt, please let me know.)

Limitations of the Anti-Forgery helpers

ASP.NET MVC’s anti-CSRF helpers work very nicely, but you should be aware of a few limitations:

  • All legitimate visitors must accept cookies (otherwise, [ValidateAntiForgeryToken] will deny their form posts). Arguably this isn’t a limitation, because unless visitors allow cookies, you probably don’t have anything to protect anyway.
  • It only works with POST requests, not GET requests. Arguably this isn’t a limitation, because under the normal HTTP conventions, you shouldn’t be using GET requests for anything other than read-only operations.
  • It’s easily bypassed if you have any XSS holes on your domain. An XSS hole would allow an attacker to read a victim’s anti-forgery token value, then use it to forge valid posts. So, don’t have XSS holes!
  • It relies on the potential victim’s browser implementing cross-domain boundaries solidly. Browsers are supposed to stop foreign domains from reading your app’s response text and cookies, and are supposed to stop foreign domains from writing cookies to your domain. If an attacker manages to find a way around this, they can bypass [ValidateAntiForgeryToken]. Of course that’s not supposed to be possible. For the most part, modern browsers block this line of attack.

In conclusion, ASP.NET MVC’s anti-CSRF helpers are easy to use, and work very nicely thank you!

68 Responses to Prevent Cross-Site Request Forgery (CSRF) using ASP.NET MVC’s AntiForgeryToken() helper

  1. Great analysis, especially your section on Limitations. We are keeping this in the Futures because we haven’t run extensive security analysis on our approach. So we provide no guarantees that it will stop all CSRF attacks. But at the very least, we think it will help mitigate the risk and we hope to vet this with the community before rolling it into core.

  2. Chris

    I’m going to assume that __MVC_AntiForgeryToken cookie has HttpOnly set. Otherwise, what’s stopping that javascript from reading __MVC_AntiForgeryToken and filling the form before calling submit()?

  3. Chris

    Duh, that’s why the XSS disclaimer. :-)
    It’s probably already an HttpOnly anyways.

  4. Steve

    @Haacked: Thanks – the technique you’ve applied is very similar to that I’ve seen tried and tested in past projects, so I can’t imagine there being any fundamental problems with it. Looks solid.

    @Chris: Actually, when I tried it, I was surprised to see that it *doesn’t* set the HttpOnly flag. But thinking about it more, this doesn’t actually affect the fact that XSS can bypass it. If there is an XSS hole in your site, an attacker can read one of your forms across domains, extract out the token, and use it when submitting that form back across domains. The cookie (and whether it is HttpOnly or not) is irrelevant to the attacker.

  5. Pingback: Reflective Perspective - Chris Alcock » The Morning Brew #171

  6. configurator

    Why not save a copy of that token in the session? I’d guess that would be a safer way (to protect against malfunctioning browsers) because the client wouldn’t have that cookie, wouldn’t it?
    Also, is it possible to validate the token by code? That way you could provide a per-user salt value, e.g. a username or password hash, which is not possible to do in attributes.

  7. Steve

    @Configurator – I guess they chose to use cookie storage rather than session storage for scalability (in web farm scenarios, you sometimes can’t use session storage). Also, session state is volatile (can be erased at any moment) whereas cookies can stick around indefinitely.

    > Also, is it possible to validate the token by code?

    Kind of, though it is awkward. You can instantiate a ValidateAntiForgeryTokenAttribute, passing a salt parameter to its constructor, then call its OnAuthorization() method. That gives you programmatic control over salt, rather than having to embed it in an attribute. It’s a bit awkward to do this because the API wasn’t designed for it – you’re expected to validate the token only using an attribute.

    But out of interest, why do you want to do this? The token contains a user-specific random value anyway, so the token is always user-specific. What extra benefit do you expect to get from having a user-specific salt value?

  8. Matt

    I’m not sure I “get it” yet. If you’re using Forms based auth (dropping a cooking on their machine), and have a filter that requires authentication of the user before they can modify their account, does this still leave you open to CSRF? (This still assumes you are preventing XSS attacks)

    Isn’t the anti-forgery token just yet-another way of authenticating the user, just like their Forms Auth token? How does the combination improve the situation?

  9. Steve

    > If you’re using Forms based auth (dropping a cooking on their machine), and have a filter
    > that requires authentication of the user before they can modify their account,
    > does this still leave you open to CSRF?

    Depending on what other things your application lets that user do, yes, you can still be vulnerable to CSRF. It’s not about authenticating the user – it’s about authenticating that requests made by that user were really intentional and not automated by a third party. Wikipedia has a decent explanation of CSRF if you need to know more.

  10. Pingback: links for 2008-09-25 « Praveen’s Blog

  11. Marco

    I don’t know how the token actually is calculated, just a Guid as in the post mentioned doesn’t seem very strong. Whta’s about calculating the token over different parts of information, making it more unique. Maybe take a combination of these:
    - User specific value as now, maybe a Guid
    - User’s IP address
    - User’S Browser Agent string (I guess nobody will change it between a GET and corresponding POST)

  12. RichB

    @Configurator: How would you identify the session? By yet another cookie? And then how would you prove that an authorized user had used that cookie to lookup the session and perform authorized-only actions? Your ‘solution’ is a chicken and egg problem :-)

  13. Pingback: More ASP.NET CSRF Protection Options -

  14. Pingback: Tweaking the AntiForgeryToken on ASP.Net MVC « The Dumping Ground

  15. I trying to use it but I have exception with DummyUrl. App is working on IIS7 and Vista. :/

  16. Steve

    @dario-g – your issue is described at The solution is to upgrade to the latest version of ASP.NET MVC (currently, the Release Candidate).

  17. This is great!!
    This is exactly what I was looking for.

    Thank You!!

  18. Ajit

    I am not able to implment in my site. I am using and C# as code behind.

    I am puuting

    in my aspx page , but I am confuse were should we put
    public ViewResult SubmitUpdate()
    // … etc
    in Whicjh file should we put above code?


  19. Steve

    @Ajit – this post is about ASP.NET MVC. You can’t use [ValidateAntiForgeryToken] with traditional ASP.NET WebForms.

  20. Regarding “salt”, the biggest benefit I see is that it provides protection against forms of dictionary attack. If an attacker can examine enough encrypted but unsalted bytes, it is more likely for them to spot a pattern and crack the code. With unique salt sprinkled in as part of the encryption, it makes duplication and/or patterns in the encrypted data harder to recognize. I think extra salt is a bit overkill though if the value you’re encrypting is itself just a random nonce used as a temporary security token.

  21. Roman Smolnikov

    You write:”This prevents CSRF because even if a potential victim has an __RequestVerificationToken cookie, an attacker can’t find out its value, so they can’t forge a valid form post with the same value in Request.Form.”
    But it seems not true. Why I can’t find out its value?
    I can simply do that:

    Where cookiename is “__RequestVerificationToken_” + encoded AppPath.
    Now I have token value and can do valid post.

  22. Steve

    @Roman – this mechanism protects against *request spoofing*, nothing else. The attack you describe would require an attacker to be able to run arbitrary .NET code on your server, which you should never allow, and if you do you have far worse security problems to worry about.

  23. Roman Smolnikov

    Ahh, sure! Attacker can’t read cookie from another domain. Ok, thanx!

  24. Ну и после такого, как говорится, хотелось бы заслушать начальника транспортного цеха ;)

  25. mmtache

    How is the request verification token user specific?

    For instance, if the token is based on the IP address, that might be a problem for users who use proxy servers for anonymous surfing.

    Ideally, you want it to be based on the username in the membership provider, so the token will always work.

  26. @mmtache – it’s a random value (like a GUID), stored in the user’s cookies, so unique to that user.

  27. I had a look at the ValidateAntiForgeryToken attribute using reflector and it has an AttributeUsage of Method and Class.

    Question is:

    What issues are inherent in applying this at the controller(class) level?

    I understand that it is irrelevant with regards GET’s(but would it cover all your POST action methods?), and I understand that you wouldn’t be able to salt forms differently.

    Applying it once is simpler, couldn’t you apply it to the controller?


  28. wysoh

    i’m thinking how would this work in a multi-tab scenario. let’s say you open a page where you have this hidden input and also your cookie is being set to value “abc”. now you open a new tab, go to a different page (or possibly the same page), the token will be set to a new value “xyz”. then you go back to the first tab and submit a request, wouldn’t that be an invalid request because the hidden input value remains “abc” but the cookie value has changed to “xyz”?

  29. Steve

    @wysoh – the random token value remains constant throughout your browsing session, so there’s no problem with multiple tabs.

  30. Bungee

    Yet another question regarding security, if I understood correctly, in order pass the AntiForgery test, you only need a form element with the same value of a cookie named __RequestVerificationToken, yet I think it is possible to create a cookie from client side with that name and just fill it with the same key stored on an input hidden of my malicious page… Would that cheat the test? Also, why don’t they just encrypt the cookie with the anti forgery token? That way, the attacker won’t have means to recreate a cookie with the same value of the form (unless he discovers the key, of course) and you can use the cookie like a certificate of the page.

  31. Thanks for the write-up!

    I’ve got a question. Is there any way to say something like [ValidateAntiForgeryToken(OnPostOnly)]?

  32. Phil Cazella

    @John Weis

    You can use multiple attributes on a given controller method in ASP.Net MVC. In this way, you can limite the Http verbs allowed to “Post” only if you wish as well as us the token.

    public ActionResult Indox(FormsCollection formData)
    //…Process the form data
    return View();

  33. JBland

    Is it possible to use this for multiple forms on the same page – say for multiple partials… is the same value used in each form ?

  34. Steve

    @Bungee – yes, to pass the antiforgery test, a request needs both a cookie and a form element with the same __RequestVerificationToken value. However, an attacker could only create such a cookie on the client if they had the means to write arbitrary cookies on your domain, which they can’t do unless you already have a XSS vulnerability. So it isn’t a viable way to cheat the test. Also, since an attacker cannot read a third party’s cookies or form values (except via an XSS hole, which would be a much worse vulnerability anyway), encrypting the token wouldn’t make any difference to the level of security provided.

    @JBland – yes, you can use it on multiple forms on the same page. The verification value is fixed for the duration of the visitor’s session.

  35. I think the AntiForgeryToken mechanism is weak. It can be bypassed in this way. A malicious user creates an HTML page like the one you described earlier, but instead he posts to a page on his own site, so he can steal the real user’s cookie, then he makes an HTTP request using HttpWebRequest on server-side creating the cookie and form fields with same random value! This should work, right?

    The other thing, why doesn’t AntiForgeryToken issue two form fields: token and hashed_token (i.e. the token and its hash value) This way even if a malicious user reveals the cookie, he won’t be able to do any malicious activity, right?

  36. Steve

    Hi Nadeem

    Sorry, but I don’t think the attack you propose would work. How would the malicious user get the real user’s cookie? You know that users have a different set of cookies for each site, and that their browsers will only post the cookies associated with the site they’re making a request to, right?

    This is the “same origin” policy for cookies, and if this were not the case, there would be much more severe problems than being able to bypass AntiForgeryToken. For example, if a malicious user could obtain an innocent user’s cookie somehow, then they could simply hijack the innocent user’s session and then manually take any actions they want on that user’s behalf. See for more details.

  37. Cross-site cooking vulnerabilities in web browsers allow malicious sites to break this rule. See for more details. We cannot assume that all browsers are safe against this type of attack.

    Anyway, the key thing I would like to say is that it is very risky for AntiForgeryToken to rely on cookies because of many reasons like XSS holes, and Cross-site cooking.

    So Wouldn’t it be much safer to use HMAC instead?


  38. Steve

    Nadeem, the same-origin policy for cookies and scripts is the only thing protecting Gmail accounts, online banking, etc. You don’t have much choice but to rely on it; it’s the only security model available.

    How would an HMAC code make this more secure anyway?

  39. Bob

    So I imagine for each AJAX post you have, you would read this token and pass it along with your AJAX call.

  40. Nadeem Afana

    Hi Steve,
    I have just seen your post. Thanks I see.
    With HMAC, one hidden input field is created and populated. The hidden field stores user ID’s (GUID) hashed value. Upon posting back, the user ID is hashed and compared with this field to test for matching. Of course, the key for hashing can be changed regularly for security reasons. The malicious user cannot guess the hashed value of the real user’s ID(maybe not even the ID itself). Also, if the malicious user tries to post back their own ID, it will be rejected. Right?

    *NOTE: Also even without HMAC, if anti-forgery were storing the value in Session collection instead of Cookies will remove the cookie alteration risk. The big drawback of this is in web farm scenarios (out-of-proc session state), data serialization/deserialization can affect performance.

    By the way, I also saw this article in your book!

  41. Steve

    Nadeem, if you’re unwilling to trust the cookie same-origin policy, then your technique doesn’t work either. An attacker might steal a real user’s authentication cookie, hijack the user’s session, and then read their hashed user ID from the hidden field in your form.

    You’ve still got to trust cookie-based authentication unless you have some alternative like Kerberos (not usually possible for public websites)or just forcing the user to enter a password on every single page (not usually acceptable).

  42. Nadeem Afana

    I totally agree with you in that, in that case, nothing can protect you if an attacker steals your cookie, they might cause damage to your site unless you force the user to enter their password on sensitive pages (an example).

    Also I do trust the cookie same-origin policy, but I also believe there is a better approach. At least, what I mentioned previously does not use COOKIES at all, so one should never worry about the browser cookie vulnerabilities. (ie when a malicious user has the ability to tamper with the real cookie using JS.)

  43. M

    @Steve: Thanks for this article–both it and the comment discussion is very informative.

  44. Pingback: Guarding against CSRF Attacks in ASP.NET MVC2 - Scott's Blog

  45. Paul Fryer

    @Steve, Any way to change the name of the cookie? In an effort to conceal our underlying technology stack, I’d like to rename the cookie to something that can’t be traced back to ASP.Net MVC.

  46. Yes it does support Windows Mobile applications. I’m not sure what kind of third party applications there might be, but there are always other third party features that are developed for something popular like Windows Mobile. You can VPN to a network if you have access to that network.

  47. Raymond

    I don’t see this working in this scenario:
    1. Attacker posts CSRF/malicious JS in another domain that does a GET on the page that contains the AntiCSRF token.
    2. Returned page/response is parsed to retrieve AntiCSRF token
    3. Token is posted with page submit along with the cookie

    Similar to what’s described here:

  48. Steve

    Raymond, step 2 in your scenario won’t happen. The browser’s same-origin policy will stop you from reading content from another domain using a GET request.

  49. eldar

    Nice post! How do I set csrf protection for whole forms in my site by default? It would be very useful if when creating view (for Edit, Create, Delete) in Visual Studio there is a checkbox “csrf protect”.

  50. Andy

    Steve, I have a question, and this is the best explanation of the AntiForgeryToken feature I have seen, so I was hoping you could answer it.

    In your article, you say that the server looks for a cookie and a Request.Form entry and compares them to see if they match. I compared the cookie value (found in Firefox’s privacy settings options) with the form value, and they were different, but it still worked as expected. I tried a couple of other pages, which generated different form values, and they all worked too, even though the cookie’s value appears to remain the same. So do you know how it compares the two? Are they encoded differently? Is one a hash of the other? Etc…

    Thanks in advance, and thanks for the great explanation!

  51. Thank you for your sharing your idea.Good things should be sharing to show its worth.
    Maybe you would like to update your garderobe,you need fashion of bags,shoes and so on,and they should be with unique and special design,outstanding.Yes,You are be warmly welcome to garderobe world,please do the link.

  52. Mahesh

    Implemented the view and controller as per the article for implementing CSRF. The application throws up the error A required anti-forgery token was not supplied or was invalid. The stack trace as below.

    [HttpAntiForgeryException (0x80004005): A required anti-forgery token was not supplied or was invalid.]
    System.Web.Mvc.ValidateAntiForgeryTokenAttribute.OnAuthorization(AuthorizationContext filterContext) +195999
    System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor) +99
    System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +399
    System.Web.Mvc.Controller.ExecuteCore() +126
    System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +27
    System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +7
    System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext) +151
    System.Web.Mvc.MvcHandler.ProcessRequest(HttpContext httpContext) +57
    System.Web.Mvc.MvcHandler.System.Web.IHttpHandler.ProcessRequest(HttpContext httpContext) +7
    System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +181
    System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +75

    Did I miss anything ?

  53. IF one site has XSS, it’s failed.

  54. IF one site has XSS, it’s failed.

  55. I dont commonly post on many another Blogs, nevertheless Thanks continue the astonishing work. Ok unfortunately it’s once again time to get to school.

  56. aliboun

    It seems quite right for preventing CSRF attacks. Due to cross domain issues, it will work as expected. Otherwise you have nothing rather than checking referer value. But you cannot rely on it. This might be the best solution until someone do better. Thanks Steve..

  57. Pingback: Securing your ASP.NET MVC 3 Application « X#

  58. Please don’t coerce me to read that story ever again have mercy.

  59. Hi Steve,

    I read your article after a long time. I suppose that @Nadeem is correct what will happen if I would implement it for whole application. So I need many session variables to stop XRSF requests. Is there any best approach or single location from where I may stop these type of request for further processing. What is your opinion on this issue?


  60. rohan

    i know its been a while this article was written and i do not know if all the over discussion still remain valid with the release of MVC 3, my solution to the delete would be to pass a model (which has an isDelete member) to my post and then iterate through the items in the model and delete the one that that has isDelete = true; this would fix everything

  61. dhawal

    Do u have piece of code for implementation in Java???

  62. Sri Harsha

    When i check the values i observe that filterContext.RequestContext.HttpContext.Request.Form[0] and filterContext.RequestContext.HttpContext.Request.Cookies[0].Value these two dont match but my request is successfull. I have cases where i need to manipulate either the requestverfication token cookie or the field

  63. ramana

    hi what is csrf

  64. ramana

    i want use Idunno.AntiCsrf.AntiCsrfModule in IIS 5.1.

    is its work on IIS 5.1

  65. yangjian

    In my MVC 3 project i have a login page that uses the anti-forgery logic built into MVC 3.

    On Firefox & Opera it works just fine, but on IE I get this:

    A required anti-forgery token was not supplied or was invalid.

  66. Isabella

    When we use this[ValidateAntiForgeryToken]
    token, it will create a cookie called RequestVerificationToken, right? How to set that cookie as “Secure” one? I need to set that as Secured one.

  67. It’s surprising that the templates provided by Microsoft still generate code that is vulnerable to CSRF.