OWASP Top10

What is CSRF ? Defending Against CSRF Attacks: Best Practices and Prevention Strategies

Introduction:

Cross-site request forgery (CSRF) is a type of security vulnerability that allows an attacker to carry out unauthorized actions on behalf of a victim. This vulnerability occurs when a web application does not properly validate user-generated requests, allowing an attacker to trick the victim’s browser into executing malicious code. CSRF attacks can result in a wide range of consequences, including data theft, account takeover, and system compromise. In this article, we will discuss in detail what CSRF is, how it works, and what steps can be taken to prevent it.

What is Cross-site request forgery (CSRF)?

CSRF, also known as “session riding,” “one-click attack,” or “sea surf,” is a type of attack where a malicious actor sends a request to a web application that exploits the user’s authenticated session with that application. In other words, CSRF allows attackers to manipulate a user’s session and make unauthorized requests on their behalf.

For example, consider an online banking website where a user logs in with a username and password. After the user logs in, they can transfer funds to other accounts, pay bills, and perform other banking-related activities. An attacker can use CSRF to trick the user’s browser into making unauthorized requests to the banking website without their knowledge. The attacker can create a webpage that contains a hidden form that automatically submits a request to transfer money from the user’s account to the attacker’s account. When the user visits this page, their browser will submit the request to the banking website, and the transaction will be carried out without the user’s knowledge or consent.

How does Cross-site request forgery (CSRF) work?

To understand how CSRF works, we need to first understand the basic components of a web application. A web application typically consists of a server that hosts the application code and a client that interacts with the server through a web browser. When a user interacts with a web application, their browser sends HTTP requests to the server, and the server responds with HTTP responses.

A typical CSRF attack involves three parties: the attacker, the victim, and the web application. The attacker’s goal is to trick the victim’s browser into making an unauthorized request to the web application on behalf of the victim. To achieve this, the attacker needs to create a web page that contains a malicious request that will be executed when the victim visits the page.

The first step in a CSRF attack is for the attacker to create a web page that contains a malicious request. This request can be anything that the web application allows, such as transferring funds, changing the victim’s password, or submitting a form. The request is typically hidden within a form or a JavaScript function, and the attacker may use various techniques to make the request more difficult to detect, such as obfuscating the code or using a URL-shortening service.

The second step is for the attacker to trick the victim into visiting the web page that contains the malicious request. This can be achieved through various means, such as sending the victim an email with a link to the page, embedding the page in an iframe on a legitimate website, or using social engineering techniques to convince the victim to visit the page.

The third step is for the victim’s browser to execute the malicious request when the page is visited. Because the victim is already authenticated with the web application, their browser will include the necessary session cookies in the request, making it appear as if the request is coming from the victim. The web application will then process the request as if it was initiated by the victim, allowing the attacker to carry out unauthorized actions on the victim’s behalf.

Example of Cross-site request forgery (CSRF) using GET request

Let’s say there is a vulnerable website that has a URL like this: https://vulnerable-website.com/change-password?id=12345&new-password=abc123. This URL allows users to change their password by providing their ID and a new password.

An attacker could create a malicious website and trick the victim into visiting it. The malicious website could contain an image tag that loads the vulnerable website’s URL with the victim’s ID and a new password that the attacker has chosen, like this:

<img src="https://vulnerable-website.com/change-password?id=12345&new-password=attackerchosenpassword" />

When the victim loads the malicious website, the image tag will send a GET request to the vulnerable website, changing the victim’s password to the attacker’s chosen password, without the victim’s knowledge or consent.

To prevent this type of attack, web developers can use techniques such as adding a random token to each form or link, using the “Referer” header to verify the origin of the request, or implementing the “SameSite” cookie attribute to prevent cookies from being sent with cross-site requests.

Example of Cross-site request forgery (CSRF) using POST request

Let’s say there’s a website called “example.com” that allows users to change their email address by submitting a form with a POST request to the URL “example.com/user/settings”.

The form might look something like this:

<form method="post" action="https://example.com/user/settings">

  <label for="new-email">New email:</label>

  <input type="email" id="new-email" name="email">

  <button type="submit">Save changes</button>

</form>

Now, imagine that an attacker has created a malicious website called “malicious.com”. The attacker knows that many users have accounts on example.com and might be logged in there. The attacker creates a form on their own website that looks exactly like the one on example.com, but with a few key differences:

<form method="post" action="https://example.com/user/settings">

  <label for="new-email">New email:</label>

  <input type="email" id="new-email" name="email" value="hacker@example.com">

  <button type="submit">Save changes</button>

</form>

Notice that the attacker has pre-filled the “email” field with their own email address, “hacker@example.com“.

Now, imagine that a user who is logged in to example.com visits the attacker’s website, perhaps by clicking on a link in an email or on a social media site. The user sees the form and thinks it’s legitimate, since it looks exactly like the one on example.com. The user enters their password (which is required to change the email address) and clicks “Save changes”.

When the user clicks “Save changes”, their browser sends a POST request to example.com with the new email address. Since the user is logged in to example.com, their session is still active, and the server trusts the request. The server changes the user’s email address to “hacker@example.com“, without the user’s knowledge or consent.

This is an example of a CSRF attack using a POST request. The attacker tricked the user into submitting a form that caused the server to perform an action that the user did not intend.

How to Test for JSON based CSRF

Testing for JSON-based CSRF (Cross-Site Request Forgery) involves verifying that the application properly validates the “Content-Type” header of incoming requests and only processes JSON data from trusted sources. Here’s an example of how to test for JSON-based CSRF in a web application:

  1. Send a Test Request with a Fake CSRF Token

First, send a test request to the application with a JSON payload that includes a fake CSRF token. The fake token should be included in the “X-CSRF-Token” header or as a parameter in the JSON data.

POST /api/endpoint HTTP/1.1

Host: example.com

Content-Type: application/json

X-CSRF-Token: fake_token

{

  "param1": "value1",

  "param2": "value2",

  "csrf_token": "fake_token"

}

  1. Observe the Application’s Response

Observe the application’s response to the test request. If the application processes the request and returns a valid response, it may be vulnerable to JSON-based CSRF.

  1. Modify the Content-Type Header

Send the same test request, but modify the “Content-Type” header to something other than “application/json”. For example, set the header to “application/x-www-form-urlencoded”.

POST /api/endpoint HTTP/1.1

Host: example.com

Content-Type: application/x-www-form-urlencoded

X-CSRF-Token: fake_token

param1=value1&param2=value2&csrf_token=fake_token

  1. Observe the Application’s Response

Observe the application’s response to the modified request. If the application rejects the request or returns an error message, it is properly validating the “Content-Type” header and is not vulnerable to JSON-based CSRF.

By following the above steps, you can identify whether an application is vulnerable to JSON-based CSRF attacks and take steps to mitigate the vulnerability. Some mitigation techniques include validating the “Content-Type” header of incoming requests, requiring a specific “Accept” header for JSON requests, or using anti-CSRF tokens in JSON payloads.

Impact of Cross-site Request Forgery (CSRF)

CSRF (Cross-Site Request Forgery) attacks can have a significant impact on both the targeted user and the application. Here are some of the potential impacts of CSRF attacks:

  1. Unauthorized Actions: An attacker can use a CSRF attack to force a user to perform an action on the application without their knowledge or consent. This can include anything from changing their password or email address to making a fraudulent purchase.
  2. Data Leakage: CSRF attacks can also be used to leak sensitive data from the application, such as personal information or financial data. For example, an attacker could force a user to make a request to retrieve sensitive data from the application and then steal that data.
  3. Reputation Damage: If an application is vulnerable to CSRF attacks, it can damage the reputation of the application and the company behind it. This can lead to loss of trust from users, which can be difficult to regain.
  4. Legal and Regulatory Issues: If an application is breached due to a CSRF attack and sensitive data is compromised, the company may face legal and regulatory issues. This can include fines, lawsuits, and loss of business.
  5. Financial Loss: A successful CSRF attack can result in financial loss for both the user and the application. For the user, this can include fraudulent purchases or stolen funds. For the application, this can include loss of revenue and legal fees.

In summary, CSRF attacks can have a wide range of negative impacts, including unauthorized actions, data leakage, reputation damage, legal and regulatory issues, and financial loss. It is important for developers to understand the potential impact of CSRF attacks and take steps to prevent them from occurring in their applications.

Preventing Cross-site Request Forgery (CSRF) Attacks:

Preventing CSRF attacks requires a combination of server-side and client-side measures. The following are some of the most effective methods for preventing CSRF attacks:

  1. Implementing CSRF tokens: One of the most effective methods for preventing CSRF attacks is to implement CSRF tokens. CSRF tokens are unique, randomly generated values that are added to each form or request that a user submits. The server verifies the token before processing the request, ensuring that the request was initiated by the user and not by an attacker. CSRF tokens can be generated and managed using frameworks such as Django, Ruby on Rails, and Spring.
  1. Checking the HTTP Referer header: The HTTP Referer header contains the URL of the page that the user came from before accessing the current page. The server can check the Referer header to ensure that the request originated from a page on the same website and was not initiated by an external website. However, the Referer header is not always reliable, as it can be easily spoofed or removed by the user’s browser or network proxy.
  1. Limiting the scope of cookies: Cookies are used by web applications to store session information and other data. By default, cookies are accessible to any page on the same domain, which means that an attacker could potentially use them to carry out a CSRF attack. To prevent this, web developers can limit the scope of cookies by setting the HttpOnly and Secure flags and by using the SameSite attribute. The HttpOnly flag prevents client-side scripts from accessing the cookie, while the Secure flag ensures that the cookie is only sent over HTTPS connections. The SameSite attribute allows web developers to specify whether a cookie can be sent in a cross-site context, which can further prevent CSRF attacks.
  1. Educating users: Educating users about the risks of CSRF attacks and how to protect themselves can also be an effective prevention method. Users should be encouraged to keep their web browsers and operating systems up-to-date, to use strong and unique passwords, and to be cautious when clicking on links or downloading files from unknown sources.

How does a developer mitigate CSRF vulnerability?

There are several techniques that a developer can use to mitigate CSRF (Cross-Site Request Forgery) vulnerabilities. Here are three common techniques:

  1. Anti-CSRF Tokens

Anti-CSRF tokens are unique tokens that are generated by the server and included in each form submission. When the server receives a form submission, it verifies that the token in the submission matches the expected value. If the token is missing or incorrect, the server rejects the request.

Here’s an example of how to use anti-CSRF tokens in a PHP form:

<?php

// Generate a random token and store it in the session

session_start();

$_SESSION['csrf_token'] = bin2hex(random_bytes(32));

?>

<form method="post" action="process.php">

  <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">

  <!-- other form fields go here -->

  <button type="submit">Submit</button>

</form>

In the above example, the server generates a random token and stores it in the user’s session. The token is then included in the form as a hidden input field. When the form is submitted, the token is sent back to the server along with the other form data. The server verifies that the token in the form submission matches the one in the session.

  1. Checking the “Referer” Header

The “Referer” header is an HTTP header that indicates the URL of the page that made the current request. A server can check the “Referer” header to verify that a request originated from a legitimate page on the same domain.

Here’s an example of how to check the “Referer” header in a Django view:

from django.views.decorators.csrf import csrf_protect

from django.http import HttpResponseForbidden

@csrf_protect

def process(request):

  if request.method == 'POST':

    referer = request.META.get('HTTP_REFERER')

    if not referer or 'example.com' not in referer:

      return HttpResponseForbidden()

    # process the form data

  # render the template for the form

In the above example, the server checks the “Referer” header to make sure that the request is coming from a page on the example.com domain. If the header is missing or does not contain the expected domain, the server returns a 403 Forbidden error.

  1. SameSite Cookie Attribute

The SameSite cookie attribute is a way to prevent cookies from being sent in cross-site requests. When a cookie is set with the SameSite attribute, it will only be sent in requests that originate from the same domain as the site that set the cookie.

Here’s an example of how to set the SameSite attribute in a Node.js application:

const express = require('express')

const cookieParser = require('cookie-parser')

const app = express()

app.use(cookieParser())

app.get('/set-cookie', (req, res) => {

  res.cookie('example_cookie', 'example_value', {

    sameSite: 'strict',

    httpOnly: true

  })

  res.send('Cookie set!')

})

app.listen(3000, () => {

  console.log('Server listening on port 3000')

})

In the above example, the server sets a cookie with the SameSite attribute set to “strict”. This means that the cookie will only be sent in requests that originate from the same domain as the server. The “httpOnly” attribute is also set to true, which prevents the cookie from being accessed by client-side JavaScript code.

Implementing CSRF Protection in Different Frameworks

Here are some code snippets for implementing CSRF protection in various programming languages:

  1. PHP

To implement CSRF protection in PHP, you can use the PHP function csrf_token() to generate a unique token for each form submission, and then include this token as a hidden field in the form. Here is an example:

<?php

session_start();

function csrf_token() {

    if (empty($_SESSION['csrf_token'])) {

        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));

    }

    return $_SESSION['csrf_token'];

}

?>

<form method="post" action="submit-form.php">

    <input type="hidden" name="csrf_token" value="<?php echo csrf_token(); ?>">

    <!-- other form fields here -->

    <button type="submit">Submit</button>

</form>

In the form submission handler (submit-form.php), you can check that the submitted CSRF token matches the one stored in the session:

<?php

session_start();

if (empty($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {

    die("CSRF token mismatch");

}

// process form submission here

?>

  1. Python with Flask

To implement CSRF protection in Python with Flask, you can use the Flask-WTF extension, which includes built-in CSRF protection. Here is an example:

from flask import Flask, render_template, request

from flask_wtf.csrf import CSRFProtect

app = Flask(__name__)

csrf = CSRFProtect(app)

app.config['SECRET_KEY'] = 'super-secret-key'

@app.route('/')

def index():

    return render_template('index.html')

@app.route('/submit-form', methods=['POST'])

@csrf.exempt # if csrf_exempt setted up as True on production env

def submit_form():

    # form submission handler

    return "Form submitted successfully"

if __name__ == '__main__':

    app.run(debug=True)

In the HTML template for the form (index.html), you can use the {{ csrf_token() }} template tag to include the CSRF token as a hidden field:

<form method="post" action="{{ url_for('submit_form') }}">

    {{ csrf_token() }}

    <!-- other form fields here -->

    <button type="submit">Submit</button>

</form>

  1. Ruby on Rails

To implement CSRF protection in Ruby on Rails, you can use the built-in protect_from_forgery method, which adds a CSRF token to all non-GET requests. Here is an example:

class ApplicationController < ActionController::Base

  protect_from_forgery with: :exception

end

In the HTML template for the form, you can use the form_authenticity_token helper method to include the CSRF token as a hidden field:

html

Copy code

<%= form_for @post do |f| %>

  <%= hidden_field_tag :authenticity_token, form_authenticity_token %>

  <!-- other form fields here -->

  <%= f.submit "Submit" %>

<% end %>

In the form submission handler (e.g. create method in a controller), you don’t need to do anything special to verify the CSRF token – Rails will automatically compare the submitted token with the one stored in the session and raise an error if they don’t match.

These code snippets demonstrate various ways to implement CSRF protection in different programming languages and frameworks. It’s important to note that these are just examples, and the exact implementation will depend on the specific requirements of the application.

Conclusion

CSRF attacks are a serious threat to web applications and can result in a wide range of consequences, including data theft, account takeover, and system compromise. Preventing CSRF attacks requires a combination of server-side and client-side measures, such as implementing CSRF tokens, checking the HTTP Referer header, limiting the scope of cookies, and educating users. By implementing these measures, web developers can significantly reduce the risk of CSRF attacks and protect their users’ data and privacy.

Akshay Sharma

Inner Cosmos

Leave a Reply