Vulnerabilities, Programming

CSRF Vulnerability: How it Works and How to Prevent it?

Cross-Site Request Forgery, or CSRF, is an attack that allows targetting web applications. Typically, it consists in making a user non intentionally execute an action in a place where he is logged in.

In this article, we will discuss some of the ways such an attack can be carried on, and how it is possible to defend against it. This article aims to provide an introduction to the topic, so all the exploit possibilities and countermeasures will not be discussed. If you want to know more, multiple references at the end of the article are discussing the subject in deep.

How to Carry on a CSRF Attack?

A Simple Example

Let's imagine that Bob and Alice are both members of a forum. For some reason, Bob doesn't like what Alice is saying and decides to give her a lesson by getting her account deleted. The platform uses open-source software, and looking at the code, Bob can see that when deleting a user, the following get request is made: /users/delete?userName=[userName]&confirm=True.

Based on that, Bob will send an email to the forum's administrator, and using a pretext, will get him to click on the URL forum.tld/delete?userName=Alice&confirm=True. Since the administrator will be logged into the forum when clicking on that link, the function to delete Alice's account will be called, and the account will be effectively removed.

Now, the account will be deleted, but the administrator will have an error message saying something on the lines of "The account has been successfully deleted", which is not very discreet. The administrator will realize what happened, and he will ban Bob.

But was there a way for Bob to carry out the attack without being that easily detected? Yes, there was. Instead of simply sharing the link in his email, he could have had sent an email formatted in HTML including a 0x0 px images:

<img src="http://forum.tld/delete?userName=Alice&confirm=True" width="0" height="0" border="0">

If the admin opened the HTML email in the same browser he is logged into the forum, the request would have been executed, and he wouldn't have been able to notice what just happened.

Note that in this example, we are mentioning GET requests, but it is possible to exploit this flaw through a POST request as well. Do do this, one would have to create a webpage with a form in HTML, and to get it to auto-submit when the page is loaded.

In Brief

To summarize, three essential elements must be present for a CSRF attack to succeed:

  • The HTTP request must be made from the browser where the victim is authenticated into the target website
  • The attacker knows which parameters are expected in the request, and what they should be. All of the parameters must be predictable
  • The target application is relying on session cookies

How to Protect Your Service Against CSRF Attacks

The most common way to deal with CSRF attacks is to use CSRF Tokens, which are an unpredictable (for a third party) chain of characters generated server-side for each request, and validated by the server for each request.

These tokens should be added to each request changing any state on the system, and should not be passed as GET (i.e., they should not be present in the URL). The main reason for this is that anything given as GET can be logged in various places, and passed along with HTTP requests. Therefore, no state-changing operation should make usage of GET requests. Instead, it is, for instance, possible to pass the token using a hidden value in a form:

<form action="//deluser" method="POST">
  <input type="hidden" name="csrf-token" value="8927dd0eeb9b65500d148bdf7a144b598fc161c974d86e1ec18174ca7813ee8483f56035e28779aae83e11017b29fe08208b09d515cafb214e5defdbace07f36" />
  <input type="text" name="username" />
  <input type="submit" name="Delete the User" />
</form>

One solution allowing to manage serverless CSRF protection is to put all of the necessary information to validate the request into the token itself, and then encrypting it. For instance, we could create a string on the format sessionId timestamp, encrypt it, and then use it as a token.

When users make a request, the server only has to decrypt the token, check that the session Id is belonging to the current user and that the timestamp is within the expected window period. If both elements are correct, then the server can go forward.

The interest in using a timestamp is to prevent reply attack. For instance, we could set a rule saying that we expect the timestamp not to be older than 5 minutes. Then, if we receive a token with a 10 minutes old timestamp, the request would be denied.

In addition to this security, it would be possible to ask for users password, or, if your application implements TOTP, to ask for a token to validate a critical operation.

Other Protection Methods

One popular way to protect services against CSRF attacks is to use double submit cookie. For each request, the server will generate a cookie with a token, and also puts this token in the request (e.g., in a form hidden field). It is based on the assumption that it is not possible to write a cookie from another domain, therefore if the cookie and the hidden fields have the same value, then the request must be valid.

This is true, but there are multiple flaws. One of those occurs if you do not control all the subdomains for your domain name, because subdomains can write cookies into the main domain, and it is not possible to easily distinguish what is written where. For instance, subdom1.dom.tld can write a cookie under dom.tld. From there, if an attacker controls subdom1.dom.tld, and your website is hosted at subdom2.dom.tld, he can write the cookie using subdom1, and it will be read by your application as a legit one generated by subdom2.

If you are wondering, the reason why using a single cookie is not enough; it is because cookies are always sent when you request a website, independently of the origin. One way to mitigate this problem (but not all the others) would be to use the SameSite Cookie Attribute, which can prevent sending cookies in certain scenarios. Note that the SameSite Cookie attribute should be implemented in addition to other measures, as it is not enough by itself.

Using the Refer Header

One other solution to prevent CSRF would be to check for the origin/refer header when validating requests, and to execute the request if the header says the request is coming from our website. This is, however, not considered to be good enough by itself, as it can run into multiple problems such as these headers being set to null for privacy, or removed by proxies.

One somewhat similar solution, when using JavaScript, would be to set custom headers when making requests. This would be working to prevent CSRF as thanks to the Same Origin Policy (SOP) implemented in browsers, it would not be possible for an adversary to create this header.

Sources

Author image

About Ixonae