Anti-CSRF Tokens to prevent Cross-Site Request Forgery (CSRF)

Anti-CSRF Tokens to prevent Cross-Site Request Forgery (CSRF)

Cross Site Request Forgery is a client-side Web Application Attack where attacker tricks victim to execute a malicious web request on behalf of himself. The attacker may send a link to the victim, with a little bit of Social Engineering, he will make the victim click on the link. Then victim unintentionally issues a request to the web server which he did not intend to do. Let’s see an example.

A victim is browsing his bank’s website into which he is currently logged in an authenticated. An attacker, sends the victim a link to a page and trick victim to visit the page using some Social Engineering, “Hey, have you ever seen this picture?”. Within that page, there will be the following line.

<img src="http://victimsbank.com/transfermoney.php?to=attackeraccnt&amount=10000" height="10px" width="10px">

After the victim clicks the link and opens the page, he will not see any image but a failed image thumbnail. But, the page has already requested the link in src attribute. So, without the victim knowing anything, money from his account has been transferred to the attacker’s account.

Anti-CSRF tokens used to prevent attackers issue requests via victim. Anti-CSRF token as a pair of Cryptographically related tokens given to a user to validate his requests. As an example, when a user issues a request to the web server for asking a page with a form, the server calculates two Cryptographically related tokens and send to the user with the response. One token is sent as a hidden field in the form and the other is sent in Set-Cookie header of the response. When the user submits the form back, these two tokens are sent back to the server, one as a GET/POST parameter (which is sent to the user as a hidden form field) and the other in a cookie. The server then compares these two tokens for forgery/malformation. If the tokens match according to the cryptographic mechanism, the server validates the request and executes the appropriate function, else server returns an error.

When we compare this functionality with the previous scenario, the attacker has no ability to guess the token’s value, since the cryptographic relationship between two tokens is unknown to the user. So he cannot send an accepted token to the server using CSRF. Since those tokens are randomly generated, although the attacker captured a previous token, he cannot use it.

Another way to do this is, instead of setting a cryptographic token in a cookie, storing it in a session variable. By this method, the server keeps a copy of anti-CSRF token using a session variable and another copy is sent to the user as a hidden form field. When the user submits the form, the server compares the hidden form field value with the token in a session variable and validates the request.

We’ll look at how this happens in the first method.

Say, when a user request for a page which contains a form, he sends following request to the website.

GET /myapp/action/transferfunds.php HTTP/1.1
Host: 192.168.64.130
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:28.0) Gecko/20100101 Firefox/28.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive

Then the server responds with the requested page with one anti-csrf token in a hidden form field and another in Set-Cookie header.

HTTP/1.1 200 OK
Date: Fri, 21 Feb 2014 07:45:52 GMT
Server: Apache/2.2.14 (Ubuntu)
X-Powered-By: PHP/5.3.2–1ubuntu4.5
Set-Cookie: 8c66e888676201a444e9697182be4bad=vuf1g409ef4gaqh74hi3rrcht1; csrftoken=e358efa489f58062; path=/
...
<form>
...
<input type="text" name="accnt" value="923349522" />
<input type="text" name="amount" value="10000" />
<input type="hidden" name="anticsrf_token" value="f10dd7316b65649e" /></form>
</form>
...

When the user submits the form, he sends one anti-csrf token as a form parameter and other in a cookie.

POST /myapp/action/transferfunds.php HTTP/1.1
Host: 192.168.64.130
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:28.0) Gecko/20100101 Firefox/28.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Cookie: 8c66e888676201a444e9697182be4bad=vuf1g409ef4gaqh74hi3rrcht1; csrftoken=e358efa489f58062;
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 96
accnt=923349522&amount=10000&anticsrf_token=f10dd7316b65649e

The server then matches two tokens for validity and performs requested operation only if successfully validated. If the anti-csrf token is missing request body when the user submits the form or anti-csrf token is changed, the server will not validate the request and raise an error as follows.

HTTP/1.1 200 OK
Server: Apache/2.2.14 (Ubuntu)
X-Powered-By: PHP/5.3.2–1ubuntu4.5
Vary: Accept-Encoding
Content-Length: 13
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Content-Type: text/html
Invalid Token

You might find different methods to prevent CSRF attacks in different frameworks written for different languages. Following is how the above mechanism employed in ASP.net applications. I used Orchard ASP.net based Content Management System and used Burp Suite to intercept requests/responses to analyze.

Log in to the application

After login as an administrator, when I request the page to change my password, the application issues an anti-forgery token to me to verify my operation.

Following is the GET request to the password changing page.

The IIS server responds with the following response. Note that server sends us an Anti-Forgery tokens pair in Set-Cookie header and a hidden form field. These two tokens are cryptographically related which only the application server knows to decrypt.

Note the Anti-Forgery token named RequestVerificationToken inside the Set-Cookie header. After the page with password change form loads, have a look at the page source and you will see there is a hidden form field called RequestVerificationToken which is the other half of Anti-Forgery token pair.

I submitted the form with a changed password. And I intercepted the request with BurpSuite. Following is the POST request to the server to change the password. You’ll see the details you entered in the form, hidden form field is also submitted. Meanwhile, in the same request, you’ll see the other half of Anti-Forgery tokens is submitted through Cookie header.

Once the server gets this request, the server uses these two cryptographic tokens to validate the request and performs the requested operation only if successfully validated. In the case of one of these tokens are not present in the above request or one of them are malformed, the server invalidates the request and returns an error.

I intercepted above request with BurpSuite, removed one token from the above request and sent. I got a 500 Internal Server Error as response and it was rendered in the browser as follows.

And also I intercepted the same request and modified the __RequestVerificationToken field. I got another 500 error in the browser as follows.

There are various open source libraries that you can use to implement Anti-CSRF tokens. Using such a well-known library will make your development easy and make the Web Application more secure.

Further references:

Cover Photo Image Courtesy: jumpem.com/website-security-analysis