In this article by Y.E Liang, the author of JavaScript Security, we will cover cross-site forgery. This topic is not exactly new. In this article, we will go deeper into cross-site forgery and learn the various techniques of defending against it.
(For more resources related to this topic, see here.)
Cross-site request forgery (CSRF) exploits the trust that a site has in a user's browser. It is also defined as an attack that forces an end user to execute unwanted actions on a web application in which the user is currently authenticated.
We will now take a look at a basic CSRF example:
python xss_version.py
Adding a new to-do item
Adding a new to-do item and posting it
To-do item is added from an external app; this is dangerous!
Adding a new to do for the Python version
Successfully injected JavaScript part 1
Successfully injected JavaScript part 2
Take note that this can happen to the other backend written in other languages as well. Now go to your terminal, turn off the Python server backend, and change the directory to node/. Start the node server by issuing this command:
node server.js
This time around, the server is running at http://localhost:8080, so remember to change the $.post() endpoint to http://localhost:8080 instead of http://localhost:8000 in external.html, as shown in the following code:
function addTodo() { var data = { text: $('#todo_title').val(), details:$('#todo_text').val() } // $.post('http://localhost:8000/api/todos', data,
function(result) { $.post('http://localhost:8080/api/todos', data,
function(result) { var item = todoTemplate(result.text, result.details); $('#todos').prepend(item); $("#todo-form").slideUp(); }) }
The line changed is found at addTodo(); the highlighted code is the correct endpoint for this section.
Trying to inject JavaScript into a to-do app based on Node.js
Successfully injected JavaScript part 1
The second alert is as follows:
Successfully injected JavaScript part 1
Now that we have seen what can happen to our app if we suffered a CSRF attack, let's think about how such attacks can happen.
Basically, such attacks can happen when our API endpoints (or URLs accepting the requests) are not protected at all. Attackers can exploit such vulnerabilities by simply observing which endpoints are used and attempt to exploit them by performing a basic HTTP POST operation to it.
If you are using modern frameworks or packages, the good news is that you can easily protect against such attacks by turning on or making use of CSRF protection. For example, for server.py, you can turn on xsrf_cookie by setting it to True, as shown in the following code:
class Application(tornado.web.Application): def __init__(self): handlers = [ (r"/api/todos", Todos), (r"/todo", TodoApp) ] conn = pymongo.Connection("localhost") self.db = conn["todos"] settings = dict( xsrf_cookies=True, debug=True, template_path=os.path.join(os.path.dirname(__file__),
"templates"), static_path=os.path.join(os.path.dirname(__file__),
"static") ) tornado.web.Application.__init__(self, handlers, **settings)
Note the highlighted line, where we set xsrf_cookies=True.
Have a look at the following code snippet:
var express = require('express'); var bodyParser = require('body-parser'); var app = express(); var session = require('cookie-session'); var csrf = require('csrf'); app.use(csrf()); app.use(bodyParser());
The highlighted lines are the new lines (compared to server.js) to add in CSRF protection.
Now that both backends are equipped with CSRF protection, you can try to make the same post from external.html. You will not be able to make any post from external.html. For example, you can open Chrome's developer tool and go to Network. You will see the following:
POST forbidden
On the terminal, you will see a 403 error from our Python server, which is shown in the following screenshot:
POST forbidden from the server side
CSRF can also happen in many other ways. In this section, we'll cover the other basic examples on how CSRF can happen.
This is a classic example. Consider the following instance:
<img src=http://yousite.com/delete?id=2 />
Should you load a site that contains this img tag, chances are that a piece of data may get deleted unknowingly.
Now that we have covered the basics of preventing CSRF attacks through the use of CSRF tokens, the next question you may have is: what if there are times when you need to expose an API to an external app? For example, Facebook's Graph API, Twitter's API, and so on, allow external apps not only to read, but also write data to their system.
How do we prevent malicious attacks in this situation? We'll cover this and more in the next section.
Using CSRF tokens may be a convenient way to protect your app from CSRF attacks, but it can be a hassle at times. As mentioned in the previous section, what about the times when you need to expose an API to allow mobile access? Or, your app is growing so quickly that you want to accelerate that growth by creating a Graph API of your own.
How do you manage it then?
In this section, we will go quickly over the techniques for protection.
Creating your own app ID and app secret is similar to what the major Internet companies are doing right now: we require developers to sign up for developing accounts and to attach an application ID and secret key for each of the apps.
Using this information, the developers will need to exchange OAuth credentials in order to make any API calls, as shown in the following screenshot:
Google requires developers to sign up, and it assigns the client ID
On the server end, all you need to do is look for the application ID and secret key; if it is not present, simply reject the request. Have a look at the following screenshot:
The same thing with Facebook; Facebook requires you to sign up, and it assigns app ID and app secret
Simply put, you want to check where the request is coming from. This is a technique where you can check the Origin header.
The Origin header, in layman's terms, refers to where the request is coming from. There are at least two use cases for the usage of the Origin header, which are as follows:
Note that the Origin header can also be modified; for example, an attacker can provide a header that is modified.
Assuming that you are generating your own tokens, you may also want to limit the lifetime of the token, for instance, making the token valid for only a certain time period if the user is logged in to your site. Similarly, your site can make this a requirement in order for the requests to be made; if the token does not exist, HTTP requests cannot be made.
In this article, we covered the basic forms of CSRF attacks and how to defend against it. Note that these security loopholes can come from both the frontend and server side.
Further resources on this subject: