Friday, May 29, 2009

Cross Site Request Forgery. What it is and how to work around it.

I was asked by my client if there's a way to solve the Cross Site Request Forgery (CSRF) issue that has been highlighted by their Internal Security Team. Previously, I help my client provide a common filter application (J2EE Filter) that filters out Cross Site Scripting Characters from a request and response. This helped them tremendously in passing the security assessment as all they have to do is define the filter on their applications and filter will do the rest.

As I have provided them the solution before, he asked me if there's a way to create a common standards for applications so that each applications need not to worry about Cross Site Request Forgery. Basically, their Internal Security Team requested them to put hidden keys that will be validated once the forms are submitted (for got post and get) and if the hidden keys are missing or wrong, then the form submission will fail.  

Anyway, this was the first time I heard of Cross Site Request Forgery, so I did some research. 

Apparently, Cross Site Request Forgery is a form of hijacking your application session in order to fool the users into submitting invalid contents to the application and the application will process it. How does it hijack your application ? And why only now ?

Basically it works this way. Imagine you are browsing your Online Banking and you need to transmit money to your GF/WIfe/Mother or whoever. You forgot the amount to transfer and the information si actullay on your Email. So you decided to check your online email account (by going to New -> Open Window on I.E., which will share the same session with the exisitng browser). While checking your email, you saw an email from someone asking you to check a good holiday getaway. You checked the email and you find that place impressive, so you decided to click on the image on that email. And that email redirected you to a genuine looking promotion site. After your read, you closed the email and decided to transfer the money.  Seems nothing happened right ? Wrong, you noticed that you have missing thousand of dollars on your account.

How did this happened ? Remember two things in these scenario :

  1. You are still logged in to your online banking. Technically, your session is still alive.

  2. You saw a genuine email inviting you to check out the new holiday getaway. However, when you clicked the image, apparently, the image link actually executes a javascript which will contact your bank by executing a get command with parameters such as : http://bank.example/withdraw?account=bob&amount=1000000&for=mallory Which actually withdraws from your account and transfers it to another account. And since your session is alive, this actually gets executed on your behalf. However, it looks harmless to you as you were just redirected to a vacation site, without knowing that you have just transferred thousands of dollars to another bank account.

Why only now ? It became prevalent because of the Tabs behaviour of the browser. Normally, what people doe before was just to launch a new IE browser (nobody goes to New->WIndow.. only a few), which thereby actually separates your banking session to your email. However, with the prevalency of the tab technology, people just open up a tab, which will actually share session with your exisitng banking application.

There are ounces of preventions on this thing. Two of the most popular ones are :

  1. Checking the HTTP Referrer.
  2. Having a hidden validation key for every form submission
The problem with checking HTTP Referrer is that this can be suppressed. Some HTTPS also omits HTTP Referrer.

Hidden Validation Key is one of the solution for this. How does this work ? Basically, for every form request, the application will issue a unique key that will be validated upon the submission of the request. Since the attacker does not know the correct key, if the form is submitted with an invalid key or missing key, the application can assume that this is an attack and fail the submission of the form.

The problem with implementing this solution is that you need to modify your application to conform to this. If you are using an MVC Framework, this might not be an issue as you can have your controller issue the key and check the key before the view generates the page. However, I don't think most of the applications out there actually uses MVC framework (take .NET for example). If you are my client and you have a lot of applications available, then you need to re-write this applications to include Hidden Validation Keys.

However, there's another solution to this issue (for both .NET and Java/J2EE). The solution is to have a J2EE Filter or IHttpModule do the job for your. How does this work ?

If you are familiar with .NET and/or Java, before the request or response reaches the application, any implemented filters via J2EE Filter or IHttpModule gets executed first. On this level, Filters can check the contents of the Request or Response. They can even modify the contents of this two. If I have a Filter that will actually help me :

  1. Generate a random key and store the key into the session.
  2. Add this Random key as a hidden value field on the Response part of the application as part of the Form Submission.
  3. When Form is submitted, I validate this key.
  4. If Key is valid, pass it to the applicaiton.
  5. If key is invalid, fail the submission.
This will solve the issue. And since its a J2EE Filter or IHttpModule, this can be re-used and is shareable to all applications. This mean... Taa-daaaa.. I DON'T even need to change any line in my existing applications to defeat  CSRF.

However, sometimes, storing a session variable may not be a good idea, especially if you are working on an environment with Load Balancing. Sometimes, session variables get lost so there's a tendency of Form Failure not because of CSRF but because of infrastructure failure. How do you do solve this ? Well, why don't we generate a Checksum key instead.

How does this work ? It works by doing the below :

  1. Read the contents of the Form Data. Based on the field names, generate a checksum with an algorithm known only to you.
  2. Add this key to the form data.
  3. When form is submitted, check the field names and calculate the checksum.
  4. If Key is valid, pass it to the applicaiton.
  5. If key is invalid, fail the submission.
Voila!!!. I don't need a session variable after all. So I told my client that this can be done.

Well, I emailed him that we can prevent CSRF without even changing your application. At most, only the configuration (web.xml or web.config) needs to be changed to include the Filter on the application.

So the filter will do this :


  1. User Request for Page
  2. Browser goes to the server and request for the page
  3. Application sends the page to the browser.
  4. However, Response Filter intercepts the response.
  5. Response Filter generates an authentication key. If you used a checksum based solution, this key is a calculated checksum.
  6. Response Filter saves the key to the J2EE Session, if you follow the session-based solution, otherwise this key is actually a checksum key.
  7. Response Filter appends the key to the HTML Form.
  8.  Response Filter sends the request to the Browser.
  9. Browser renders page (with the key embedded).
  10. User interacts and submits the page
  11. Information is send to the application
  12. However, Request filter intercepts the request.
  13. Request Filter checks for the authentication key
  14. Request Filter Authenticates Key by comparing with the J2EE Session, if you're using a Session-based solution, otherwise Filter will validate the checksum..
  15. If key is invalid, Request Filter generates a response and shows error page. Request ends.
  16. If valid, Request Filter sends the information to the Application.
  17. If Key is in the hidden field, hidden field is removed. This is optional and can be done in-cases where Application checks for any extra fields and invalidates the request if any extra-field is found.
  18. Application processes the information.
Next would be coding part. When I have time, I will probably code on both .NET and Java and share the codes to you. It will be a simple Filter that uses Session-based solution.

2 comments:

  1. Hi James,

    It was a nice article. But I didn't get one thing. Even if we use filters to avoid XSRF the validation token is embedded in the form right. Still the hacker can send the same validation token along with his request. Correct me if I am wrong.

    Best Regards,
    Raghavan G

    ReplyDelete
  2. The token is supposed to be re-generated for every request submitted. This means that, for every request, a different key is generated. This same key is stored on the session. Also, the key generated on the FORM is not literally the key saved on the session. Think of it as PKI, Form has the public key and the session has the private key. There should be some sort of calculation so that it's tougher for the hacker to generate the correct key himself.

    ReplyDelete