Home / 

In continuation of the blog series highlighting high-risk vulnerabilities that exist in ASP.Net and PHP web applications, this blog is third in the series that talks about CSRF vulnerabilities that exist in the websites.

Understanding CSRF vulnerabilities

Forgery is an “act of copying or imitating things like a signature on a check, an official document to deceive the authority source for financial gains”. CSRF is the website forgery where a malicious user forges a request on behalf of another user by using social engineering to gain complete access of the application, eventually taking complete control of the web application. Other use cases for CSRF are funds transfer from another account, change of passwords, and data theft.

Web applications can send requests to another web application without showing any response. Since this attack is only possible when a user is logged in and has a valid session cookie, the malicious user requests the web application on behalf of the logged-in user while sitting on another website. This vulnerability is so critical that the Open Web Application Security Project (OWASP) has included it in its top 10 vulnerabilities list.

How the attack occurs?

When a web application stores session information in a cookie, the cookie information is sent to the web server with every request the client has made. It uses the same-origin policy. Since the cookie is not entirely secure, it is vulnerable to be leaked or reused by a malicious user when an authenticated user visits a malicious website.

Let us take an example. A user is logged into an e-commerce website. After some time, the user closes the browser tab and starts browsing some other website. Meanwhile, he receives an email with a link to a malicious website. The moment he visits the website, an order is made on behalf of the user on the e-commerce website.

Since the user did not log out from the e-commerce website and his session was still valid, the malicious website makes requests on behalf of the user to order products using the user’s session. Transaction happens silently in the background and a specific amount is deducted from the user’s bank account. The user is unaware of the transaction until he receives any related notification.

Real-world scenario of the attack

  1. Jack visits xyz.com, logs in, and leaves the site without logging out.
  2. Jack visits foo.com (a malicious website) which inadvertently executes the requests to xyz.com from Jack’s browser.
  3. The victim’s browser sends this request, along with all victim cookies. The request seems legitimate to xyz.com.

Real-world CSRF attack

Let‘s assume that the URL mentioned below is required to make the successful GET request to xyz.com to buy a book. The link provided given below will only work when a user is successfully authenticated with xyz.com.

https://xyz.com/buy.php?bookid=123&transfer_amount=99999

Since Jack is still logged in on xyz.com, the request is sent and money is withdrawn from his account for the purchase of the book.

All requests to a web application that do not implement an anti-CSRF mechanism are automatically vulnerable. Now let’s assume that it is a POST request instead of a GET request.

Code:


POST /buy.php HTTP/1.1
Host: xyz.com
User-Agent: Any Webbrowser
Cookie: phpsessid=SSDFE23UN5VYS8P0JE2V6SG&lastvisit=1398310259&user=Jack
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 47
bookid=123&transfer_amount=99999


Now, some developers may think that making a POST request instead of GET request would make the web application more secure. But that is not the case when there is a CSRF vulnerability in the web application.

When a web application stores session information in cookies, these cookies are sent in every request to the web application (same-origin policies applies), storing session tokens in cookies makes CSRF exploitable.

Hence, it is very crucial to always log out from the website when you are done with your work. Website security was unable to help the user in this case because everything seems authentic as the request was made by a valid and authenticated user for xyz.com.

Drawback of saving User’s Identifier value and set it in the cookie

Suppose, an HTTP Flag is set to “False” and JavaScript was able to read cookie details. The malicious user can use the session information to either privilege escalation in the web application or sniff the traffic to read the cookie value. In that case, both the users will have the same session value which is valid for the web server. That is when the CSRF attack occurs when a web server is unable to authenticate between a legitimate user and malicious user.

How to prevent CSRF vulnerability?

Double Submit Cookie pattern: An authentic user creates a session and sets Session ID as a cookie. Along with that, another cookie is generated by the server - random unique string generated or Nonce (number used once discarded) – which is set as an anti-CSRF token on the user’s machine. The token value is unpredictable since it changes with every request. HTTP flag is set to “false” so that JavaScript can read and set the value of the anti-CSRF token in the hidden form field.

This cookie is not stored by the server. Any state changing operations will extract this token during POST request and send it to the web server. The value of both cookie and token are validated by the web server. Request is rejected if both the values failed to compare or when an anti-CSRF token is not present in the request body.

In a real-world scenario, Update User form includes a hidden input that requires a token. This token can be implemented as MD5 hash of randomly generated string. The token is saved in session variables.


<form action =”updateuser.aspx” method = “POST”>
<input type=”text” name=”username”/>
<input type=”password” name=”password”/>
…..
<input type=”hidden” name=”token” value=”03d7022931447a88b1ae1a6f6b85b060” />
</form>

UpdateUser.aspx will check whether the cookie having Session information matches the token received through a POST request. The request is fulfilled if they match, otherwise, it is refused.

Request sent to the web server by an authenticated user to update his profile details

  • Authenticated user fills the form and submits the request.
  • Cookie has the user’s Session information and sends it in the header of the request.
  • Token (anti-CSRF) value is sent as a part of the request body.
  • The web server would compare an anti-CSRF token with a cookie present in the header. User profile details are updated if the comparison is successful, otherwise, it is refused.

Authenticating the user


User sends the updated profile

Using this pattern, the malicious website will not be able to guess the anti-CSRF token value. The malicious user may be able to send the request on the behalf of the user, but it will not be able to get the response back since such requests will be refused by the web server due to the missing anti-CSRF token or the token not matching with cookie settings in the header.

A generated token is never saved on the server-side; it changes with every request. To prevent CSRF, one has to implement the one-time token for every request. One can also implement captchas on the website before making a payment or updating user details to ensure that request is made by a real user and not by an automated request.

Implementing Anti-CSRF tokens in Model-View-Controller (MVC)

After implementing an anti-forgery token in a web application, the malicious user will not be able to read the anti-CSRF token due to same-origin policies. The origin should also be verified by the web application to understand whether the request is coming from the same domain or not.

Below is the implementation of an anti-forgery token in ASP.net:

In view, we will use anti-forgery tokens to update the User Profile details.


<html>  
<head>
  <title>Update User Profile</title>
</head>
<body>
  <div> Update Email 
    <form action="Account/UpdateUser" method=post> Email <input type="text" name="email" value="" /><br />
    @Html.AntiForgeryToken() <input type=submit value="Update" /> </form> 
  </div> 
</body>
</html>


An HTML, Anti-Forgery Token field look likes:


<input name="__RequestVerificationToken" type="hidden" value="EW4QS1DQk7LRlXhB-
r4ddwfsffmsRfbIKF2aTWGklW-SuiQYFKVJhZ564lOZP_KD6_Gh3kkF4MbT4YbfIsjqicKj239d8WH5Tj7IknOLXt-G-
iU3fR5OFCn9_Itbhw1">
    

Now, we will decorate the Update action method with ValidateAntiForgeryToken attribute in Account controller in UpdateUser method which is using POST request.


[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Update (string username)
{
//Update user email
  return Content (Request.Form["email"] + " has been updated);
}
    

When the form is submitted, the ValidateAntiForgeryToken attribute checks both the form elements and cookies for RequestVerficationToken. It ensures that both of them has the same value. When validated successfully, the action is allowed to proceed, otherwise, ValidateAntiForgeryToken attribute throws an exception:


Server Error in '/' Application.
________________________________________
The required anti-forgery cookie "__RequestVerificationToken" is not present.
    

Thus, It is recommended to use an anti-CSRF token in every POST request.

To summarise, CSRF is one of the critical vulnerabilities that exist in today’s web applications. Attackers target specific e-commerce websites to steal money or buy products from an authenticated user’s account. Such vulnerabilities reduce the trust of the user on the e-commerce website; it also impacts the relationship between the user and website in the long run.

To mitigate the risk and keep users’ trust, the websites must implement corrective measures to wipe out any chances of CSRF vulnerability in web application code. In the absence of anti-CSRF tokens, the web application should be able to deny any request, even if the request seems legitimate. We will see more critical vulnerabilities in the upcoming blogs and how those can be mitigated in the right way.