I was at the MIX’09 this year thanks to Lynn Langit. There were so many interesting sessions however I was more interested in the MVC ones. I attended both Scott Hanselman’s blog an Phil Haack’s sessions. They were great, one of the nice thing was CSRF protection in asp.net mvc framework.
What is CSRF? CSRF (Cross site request forgery) is an attack that tricks the victim into loading a page that contains a malicious request. CSRF is a confused deputy type of attack. It basically means, if Bob wants to kill Alice, he doesn't have to do it himself, if he can convince Sheriff that Alice is a bad person, the deputy will be confused and do the job for Bob.
The attacker hopes that you will be logged into your account (any account that he will be targeting), then tries to trick your browser to do a post or if the website isn't programmed good a get request on your behalf. This attack is usually combined with XSS (cross site scripting) attacks. A very simple example of this would be this:
<img src="http://stocks.com/buy.aspx?sym=MSFT&shares=500">
Over here the attacker assumes a couple of things:
- The victim has an account with stocks.com
- The victim is logged into his account, or selected the remember me section in the login page
- stocks.com is bad designed that a get request is good enough to buy shares
- The victim will somehow see visit our page to activate this link
Number 4 is easy, as all we need is either start sending spam emails saying that if you send this to 10000 people Microsoft will be donating some big bucks to a cancer sick kid, or go to a forum(s) that has XSS vulnerability, and create an account with the signature above, and start posting messages so whoever loads the message will be calling the image source. Number 1 is not a bad assumption either, if we send our message to enough people, even if 0.1% of people has a stocks.com account, that would be enough. Number 3 is… just think how many of you as programmers check post vs. get.
Do you think nobody will make these mistakes? Do you know about Sammy attack? One guy attacked myspace with CSRF and in less than 24 hours he had millions of friends.
What are the typical defense techniques against CSRF attacks?
- Check referrer: This is not always possible as some companies (AOL) changes the referrer in the http header.
- CAPTCHA: using those weird written characters on the page and asking the user to type in what they see, may be a solution but it is not accessible (and if you are building a site for government your site must be accessible)
- Password reauthentication: Before critical operations such as buy this stocks, complete transaction etc, ask the user to enter his password again.
- Dont use GET: For critical operations such as buy stocks, complete transactions etc, don't accept get request, but require post requests. This will not solve the problem, but will help with the solution.
- ViewStateUserKey: this is one the hidden gems in asp.net web forms i guess as only a few of the developers know about this. This is a property of the page class, and it assigns a unique identifier to each user and keeps it in the viewstate. You can only set this value at the Page_Init event, Page_Load is too late, and will throw an exception. Basically you can just assign the session id to this variable at page_init, and nature of asp.net web forms will check the viewstate , and if it is been modified throws and error. This works only on post actions
How about ASP.NET MVC?
If you watch Phil Haack’s MIX’09 session, he explained the defense technique in details. Basically ASP.NET MVC has an anti-forgery helper. The algorithm works like this: Put a value in the cookie, trusted modern browsers don't let cross domain cookie shares, put the same value in the form as a hidden value, in the postback compare these 2 values. Let’s say your action method is CompleteCheckout(), and you want to protect this function from CSRF attacks. First thing you do is to apply filter attribute: ValidateAntiForgeryToken, so your action method would be like:
1: [AcceptVerbs(HttpVerbs.Post)]
2: [ValidateAntiForgeryToken]
3: public ActionResult CompleteCheckout()
4: {
5: billing.ChargeCreditCard(shoppingCart);
6: shipping.ShipToClient(shoppingCart);
7: return View();
8: }
This filter basically means, check the value in the cookie, and compare it with the value from form value. As a second step we have to put the value in the form field, there is an helper method for this, and the syntax is simple:
<%=Html.AntiForgeryToken()%>
When you call this helper function from your view page, it generates a hidden type with the name “__RequestVerificationToken” which is a base64 encoded value. And that’s it :) Now if the values do not match, the framework will throw an exception and the attacker couldnt confure the deputy :)
Thanks to Phil’s team to make this so easy.