Cross Site Request Forgery

10/17/2015 05:26:00 PM
Tweetable
One of the most important and least talked about web security issues is cross-site request forgery (CSRF). For some reason, it seems like web security experts and commentators spend a lot more time talking about things like cross-site scripting (XSS), which are actually a lot harder to pull off than CSRF. So here's my attempt to understand CSRF.

Like a lot of web security issues, the story begins with the same-origin policy. All browsers enforce a policy whereby they will block http requests from a site with one domain name to a site with a different domain name, unless the response from that outside domain says to honor the request. An important caveat, though, is that the browser only blocks the origin site from reading the response from the second domain if the second site does not allow cross-domain requests. Since the browser cannot know ahead of time what the second domain's cross-domain policy will be, it can't block the first site from making the request.

So consider the following scenario: you sign into facebook, the site places an authentication cookie in your browser which your browser then sends with all requests to facebook.com to verify to facebook.com that you are in fact you, and that you have authorization to view and control the account. Browsers automatically send all cookies that belong to the domain that an http request is going to. This means that when an interesting-looking news article comes up in your facebook feed and you open it up in another tab, the javascript in that site can actually make an http request to facebook.com, and your browser will actually send your real, valid authentication cookie along with this forged request! Facebook.com, unable to tell what domain the request was made from, will actually reply with the real corresponding information from your facebook profile. Fortunately though, the browser will read the reply and see that facebook is not authorizing this third party domain to read it, and will block the other site from reading any your private facebook information.

Unfortunately, that same-origin policy comes too late in the process for other types of requests. Suppose that third-party website you clicked on instead sends a POST request containing a new facebook status update. Facebook.com would receive that request, see that it has a valid authentication cookie, think you made it from their website, and post the status under your name. That's very bad. It's easy how this can be an immensely profitable vulnerability when it comes to, for example, POSTing shopping orders on amazon.com or editing administrative records in a corporate web system.

How do we block this type of attack? The puzzle is we need a way to verify that the request was made from a web page under our control. Here's a way to do that: require that the request content contain a randomized key that matches a key in the request cookies. As mentioned earlier, browsers send cookies along with each request based on the domain the request is going to. So facebook.com can set such a key in the user's cookie collection for facebook.com, and then send this key back in the request body whenever it makes a POST request. The third-party website, however, is unable to read the key from the cookie collection for facebook.com, because browser blocks it from reading such cross-domain requests. But the browser does send this key back as part of the cookie collection for any requests to facebook.com. Thus by checking to see that the request has a key that matches the one sent in the cookie collection, we can confirm that--provided the browser properly implements the same-origin policy--the request came from the same domain. In asp.net, these keys are known as anti-forgery tokens. They are part of the asp.net MVC assembly. Click here for more on how to use anti-forgery tokens to prevent CSRF.

To sum up, you should be using anti-forgery tokens for every POST, PUT, and DELETE http request. Even for things like logins, since a CSRF could potentially log users into the wrong account--a variant of phishing that could lead a user to accidentally expose sensitive information to a different user. Every time, for everything other than GET. And you should make sure your GETs are true GETs--they should have zero effect at all on the state of your data on the server.