Tuesday 21 May 2013

CSRF (Crost Site Request Forgery)

The problem:
Someone sends you an email with a link to their own website, on that site there's a link that tries to do something malicious to another site.

E.g. One can easily create a page with the following elements:
<a href="http://facebook.com/changePassword?newPassword=iknowyourpassword">

Obviously this is just a simple example, and your clearly can't hack facebook like this, but it demonstrates exactly what CSRF (Cross Site Request Forgery) is.

What is CSRF?
CSRF is basically, a site sending request that should normally only be sent from pages originated from the same domain.  In the example above, that path should probably only ever show up from the GENUINE facebook password change page.


How do we protect against it?
Clearly it doesn't take much for someone to go to the facebook change password page, and look at the source and try to mimic the same request on their website.  So how do we protect against it?

Firstly, we need to look at 2 different categories of requests: Simple and Complex (these aren't technical terms.. but it seems to be how people are referring to it)

The answer is CORS (Cross Origin Resource Sharing)

Simple
Simple request is basically, GET or POST with certain content-type header (e.g. application/x-www-form-urlencodedmultipart/form-data, or text/plain)

When you submit a simple request, the "Origin" header will ALWAYS be included, then its up to the server to decide whether they want to allow access to the resource from this domain.

The server will also send back some headers about who can send what.... so that the client knows what it is allowed and not allowed to access eg:
Access-Control-Allow-Origin

Complex 
Complex requests (i.e. anything not simple) are "pre-flighted", this means that all modern browser when a request is Cross Domain it will first send an "OPTION" request to the server asking for permission before actually sending the request.

On the Server side

With simple requests there is NO way (using modern browsers), to add custom headers to it.

So we can easily on the server side check for the existence of a non standard header, and if its there we know for sure that its not a simple request.  If its not a simple request then we know that the request had been pre flighted which means they had already asked for and been granted permission to use this resource.

If it doesn't have a custom header, then it can be a simple request in which case you probably want to check the origin header, or use a unique token (see below).

GET requests are considered "safe" (not idempotent), because a GET request shouldn't change anything.. i.e. you don't go an change password with a GET request.  So GET requests does not need to be protected.

CSRF Token
Another way to secure your web resources is through the use of a token.
The token can be rendered on to the page served up by your site, and needs to be included when you send a request.  The server (probably using a filter or something), will always check the token ensuring that it exists and that its the correct value.  The token value can be session based, i.e. you generated once for the entire user's session.

So what if someone just sends a GET request and programmatically scans the page for your token and then constructs a link with the token??

Short answer is they can't.  Apparently, you actually cannot programmatically read the response of a Cross Domain Request)!  Your browser can render it and display it on your screen, but you can't actually write some scripts to parse it!  This means they cannot programmatically extract the token and use it to construct a link!

Custom Headers

  • You can only include the header if you're calling from JS
  • Same Origin policy prevents calling outside the domain from JS
  • If you're in the same domain, CSRF is no longer the problem since you can get the token anyway