Information Security, Web, Networks and Systems

Friday, February 21, 2014

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. Attacker may send a link to the victim, with a little bit of Social Engineering, he will make victim click on the link. Then victim unintentionally issues a request to the web server which he did not intended to do. Lets see an example.

A victim is browsing his bank's website into which he is currently logged in an authenticated. An attacker, sends 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 following line.

<img src="" height="10px" width="10px">

After victim clicks the link and open 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 victim knowing anything, money from his account has been transfered 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 users issues a request to the web server for asking a page with a form, server calculates two Cryptographically related tokens and send to the user with the response. One token is send 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 paramter (which is sent to the user as a hidden form field) and the other in a cookie. Server then compares these two tokens for forgery/malformation. If the tokens match according to the cryptographic mechanism, server validates the request and executes the appropriate function, else server returns an error.

When we compare this functionality with previous scenario, 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 attacker captured a previous token, he cannot use it.

Another way to do this is, instead of setting a cryptographic token in cookie, storing it in a session variable. By this method, 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 user submits the form, server compares the hidden form field value with the token in session variable and validates the request.

We'll look at how this happens in 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
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 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=/

<input type="text" name="accnt" value="923349522" />
<input type="text" name="amount" value="10000" />
<input type="hidden" name="anticsrf_token" value="f10dd7316b65649e" /></form>

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

POST /myapp/action/transferfunds.php HTTP/1.1
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

Server then matches two tokens for validity and performs requested operation only if successfully validated. If anti-csrf token is missing request body when user submits the form or anti-csrf token is changed, 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 the above mechanism employed in applications. I used Orchard based Content Management System and used Burp Suite to intercept requests/responses to analyse.
Login to the application

After login as administrator, when I request the page to change my password, the application issues an anti-forgery token to me to veryfy 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 a Anti-Forgery tokens pair in Set-Cookie header and a hidden form field. These two tokens are cryptographically related which only 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 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 besides 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, server uses these two cryptographic tokens to validate the request and performs the requested operation only if successfully validated. In case of one of these tokens are not present in the above request or one of them are malformed, server invalidate 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 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:
Fixing CSRF Vulnerability in PHP Applications
OWASP - Cross Site Request Forgery
OWASP - CSRF Prevention Cheatsheet