Learning Resources for Software Engineering Students »
Author: Tran Tien Dat
Cross-Site Request Forgery (CSRF) is a dangerous type of attack that has affected major sites like Gmail and Netflix in the past. This article attempts to give an easy-to-digest introduction to the attack and how to protect your website from it.
A CSRF attack tricks the victim to perform actions that they do not intend to do on a web application in which they're currently authenticated. It generally consists of 3 steps:
These steps are best explained through an example. Suppose that Alice is a customer of the banking website
www.example-bank.com. Alice wants to transfer money to her friend Bob (because she does not like the fact that he keeps paying for her meals when they go out together).
Alice first logs in to the website at
www.example-bank.com/login, providing her username and password. She then clicks on a hyperlink on the site to go to the URL
www.example-bank.com/transfer to perform her transaction. Note that Alice does not have to enter her username and password again, but the bank still knows that the request is made by her. This is done through a mechanism of the HTTP protocol called a HTTP Cookie. A cookie is a small piece of information that is sent along with all HTTP requests to a particular website. In our case, after receiving Alice's credentials for logging in, the bank instructs Alice's browser to store a cookie
auth=1abcd2ek3292fsa390sdf and send this cookie together with every subsequent request to
www.example-bank.com. Thus, once the web server sees this cookie in the request, it knows that this is an authenticated request from Alice. Many websites use this cookie mechanism so that users only have to log in once.
www.example-bank.com/transfer, Alice fills out the following HTML form to transfer money to Bob:
<form action="/transfer" method="POST"> <input type="text" name="receiver-name" value="Bob"/> <input type="number" name="receiver-account-no" value="123456"/> <input type="number" name="amount" value="100"/> <input type="submit"/> </form>
As Alice clicks the submit button, the browser will send the following request to
POST http://www.example-bank.com/transfer HTTP/1.1 Cookie: auth=1abcd2ek3292fsa390sdf receiver-name=Bob&receiver-account-no=123456&amount=100
The bank's server verifies that the cookie
auth=1abcd2ek3292fsa390sdf is valid and is associated with Alice. It then proceeds to transfer $100 from Alice's account to account number 123456, which belongs to Bob.
Alice then receives a chat message from her not-so-trustworthy friend, Eve:
Eve: OMG Alice! This site is selling the shoes you have always wanted for half the price: www.i-am-not-evil.com/shoes
Excited by this piece of news, Alice clicks on the hyperlink to check out
www.i-am-not-evil.com, Alice clicks on the
View more pictures button on the website to see more pictures of her favorite pair of shoes. Unbeknownst to her, that button has the following HTML code:
<form action="http://www.example-bank.com/transfer" method="POST"> <input type="text" name="receiver-name" value="Eve"/> <input type="number" name="receiver-account-no" value="987654"/> <input type="number" name="amount" value="100000"/> <input type="submit" value="View more pictures"/> </form>
So, when she clicks that button, the browser actually sends the following request to
POST http://www.example-bank.com/transfer HTTP/1.1 Cookie: auth=1abcd2ek3292fsa390sdf receiver-name=Eve&receiver-account-no=987654&amount=100000
Since Alice is still logged in to
www.example-bank.com, the browser automatically attaches the cookie
auth=1abcd2ek3292fsa390sdf to any requests made to
www.example-bank.com. Hence, the bank's server considers this as an authenticated request from Alice and proceeds to transfer $100,000 from Alice's account to account number 987654, which belongs to the attacker!
From the example, we can see that a CSRF attack works by forging a valid request which inherits the identity and privileges of the victim on the vulnerable web server. Such inheritance of privileges is possible because the browser attaches any credentials associated with a website to all requests made to the site. Therefore, if the user is currently authenticated to the site, the site will have no way to distinguish between the forged request sent by the victim and a legitimate request sent by the victim. In short, there are 3 conditions necessary for the attack:
Note: Social engineering refers to psychological manipulation of people into performing actions. In our example, Eve shares a link with a description that matches Alice's interest, which increases the chance that Alice will follow the link. Thus, this action is a form of social engineering.
Both users and websites can take actions to defend themselves against CSRF attacks. As the user, one can reduce the likelihood of being attacked by logging out of sensitive services like banking immediately after carrying out transactions. This will negate condition number 2. Condition number 3 can be mitigated if the user is extremely careful and never visits websites they do not know. However, users generally do not follow this and just click on interesting links they encounter on social media. Hence, this threat of social engineering could never be fully eliminated.
As for the web server, it can eliminate condition number 1 by adding a secret token as a hidden field to all forms that it serves. This token should be random and cannot be guessed by the attacker. Back to our example, the bank website should use a form like the following:
<form action="/transfer" method="POST"> <input type="hidden" name="csrf-token" value="abcd23ksk3l2faad2kdkl"/> <input type="text" name="receiver-name" value="Bob"/> <input type="number" name="receiver-account-no" value="123456"/> <input type="number" name="amount" value="100"/> <input type="submit"/> </form>
So a valid request by Alice made from the bank's own website will also carry this secret token
csrf-token=abcd23ksk3l2faad2kdkl. Since the token is added by the bank's website on its webpages, forged requests from other websites will not know the token. Thus, when the bank's server receives a request, it can distinguish between a legitimate and a forged request by checking that the CSRF token is present and valid.