Cross-Site Request Forgery (CSRF) also known as session riding or one-click attack is a security attack that executes unwanted actions on a web application on behalf of a logged-in user.
To understand this, take this scenario: suppose there is a user logged-in to their account (perhaps on a social media application). To track user sessions, the application stores cookies in user’s browsers. When a user gets authenticated, a cookie is saved on their browser and on subsequent calls that they make to the application, the cookie gets sent with the request.
The cookie contains identification data that lets the application know who the user is, the actions they can perform and what they have access to. The cookie gets reused until it expires, gets invalidated or is deleted by the user through their browser settings.
Now suppose, the logged-in user was surfing the internet and they happened to be on a website made to try to perform CSRF attacks on the social media website the user uses. This malicious website won’t make this obvious of course, it will most likely be an innocent-seeming website that runs scripts on the background.
Let’s say the website is a file-sharing one. It would carry out the attack by having on-click listeners on links that users are likely to click on. When the user clicks on the link, a request will be made to the social media application with the user’s stored cookie. When the app gets the request, it will honour it since it contains a valid cookie. The attackers will thus be able to run commands on the user’s behalf.
Examples of Famous CSRF Attacks
The following are some famous well-documented CSRF attacks according to Wikipedia.
- The Netflix website in 2006 had numerous vulnerabilities to CSRF, which could have allowed an attacker to perform actions such as adding a DVD to the victim’s rental queue, changing the shipping address on the account, or altering the victim’s login credentials to fully compromise the account.
- The online banking web application of ING Direct was vulnerable for a CSRF attack that allowed illicit money transfers.
- Popular video website YouTube was also vulnerable to CSRF in 2008 and this allowed an attacker to perform nearly all actions of any user.
- Customers of a bank in Mexico were attacked in early 2008 with an image tag in email. The link in the image tag changed the DNS entry for the bank in their ADSL router to point to a malicious website impersonating the bank.
- McAfee was also vulnerable to CSRF and it allowed attackers to change their company system.
How to Defend Against CSRF Attacks
There are different ways one can counter CSRF attacks. A common approach is the inclusion of additional authentication data in requests an application makes to and from users. This is usually done in the form of a Token that is generated on the server and is included as an HTTP parameter in requests. The token is sent to clients and when the clients make a request to the server, they have to send back the token which is first verified before the user action can be carried out.
There are different ways of generating tokens on the server, but they all must ensure that the generated token is unpredictable and unique. This is usually done by encryption with a unique key. The server usually generates a random value and encrypts it using a secret key, before sending it to the client. If the resulting token is tampered with in any way, or if someone tries to send back a token encrypted with another key, the server will not be able to decrypt the token and so validation will fail and the request will be rejected. When validation fails, the server sends back a 400
error.
Lucky for us, sometimes you don’t have to do much to have basic CSRF protection. Most web application frameworks and libraries come with token-based CSRF protection enabled or at least built-in. For example, Ruby on Rails has CSRF protection enabled and ASP.NET MVC comes with built-in CSRF protection mechanisms, but you have to enable it yourself. In Python, if you are using such a library as WTForms or Flask-WTF for your Webforms, then CSRF protection will be enabled by default. Because of this, when creating a web app, you should weigh the pros and cons of using an already established framework and writing a solution from scratch. Frameworks and libraries come with the advantage of having a lot already done for you including having some security measures implemented. Be sure to check the documentation of whatever framework you are considering to see the security measure it has in place.
Other ways of increasing your web application’s defense against CSRF is through the following options that require user interaction for a valid request to be sent.
-
Re-Authentication: Your web application could request the user to log in again when they are about to perform some high-risk operation.
-
Use of One-time Token: With this, a token is generated for every request made. This kind of application doesn’t keep user session and so reduces the chance that a malicious script will be able to get a hold of a valid session to use. The disadvantage of this is that users will have to log in each time they use the application, thus it affects the overall user experience.
-
Anti-automation: This is commonly done with such things as using CAPTCHA that prevent requests to be made to the server unless some criteria are passed. Some checks are made to ensure that actions are carried out by a human and not a bot script.
Handling Ajax Requests on the Frontend
On the frontend, the CSRF token is usually added as a hidden field on forms. When such a form is submitted, the token is sent to the server as well, so that the request can be validated.
If you are posting the form via Ajax, you need to make sure that the token get sent as well. You should add the X-CSRFToken
header to the request. Below you can see how you would configure jQuery to send the token in all requests.
<script type="text/javascript">
var csrf_token = $('meta[name="csrf-token"]').attr('content');
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrf_token);
}
}
});
</script>
The above assumes that the token is saved in a meta
tag with the name csrf-token
. Before each request, the token is added to the request header.
Another way of doing it is to provide a callback function to the ajaxPrefilter()
method which will be called on every Ajax request.
var setCSRFToken = function(securityToken) {
jQuery.ajaxPrefilter(function(options, _, xhr) {
if ( !xhr.crossDomain )
xhr.setRequestHeader('X-CSRF-Token', securityToken);
});
};
setCSRFToken($('meta[name="csrf-token"]').attr('content'));