Preventing CSRF attacks: Best practices and techniques

Preventing CSRF attacks: Best practices and techniques

What is Cross-Site Request Forgery (CSRF)?

CSRF is like a ninja that sneaks into your web application and steals information or performs actions on behalf of a user without their knowledge.

Imagine you're at a restaurant and the waiter comes to take your order. But what if someone else was able to tell the waiter what to order for you without you knowing? That's what CSRF does - it tricks the web application into accepting commands from an attacker instead of the user.

A CSRF (Cross-Site Request Forgery) attack happens when a malicious actor tricks a user's web browser into making a request to a web application that the user did not intend to make. This attack is also known as a "session riding" or "one-click" attack.

Let's have a look at how CSRF attacks happen. Below is a step by step instructions on how a CSRF attack is carried out.

  1. The attacker creates a website or email that contains a link or form that appears to be legitimate, such as a "submit" button on a form or a link to a login page.

  2. The user, who is logged into the vulnerable web application, clicks on the link or submits the form, thinking that it is a legitimate request.

  3. The user's web browser sends the request to the vulnerable web application, along with any authentication cookies or other session tokens that are required for the request to be authenticated.

  4. The vulnerable web application receives the request and assumes that it is legitimate since it contains the necessary authentication tokens.

  5. The vulnerable web application performs the action specified in the request, which could be anything from changing the user's password to transferring money out of the user's account.

What makes CSRF attacks so dangerous is that the user may not be aware that the attack has occurred. The user may think that they are simply submitting a form or clicking a button, without realizing that their web browser is also sending a request to a vulnerable web application in the background.

Prevention Techniques

There are several techniques that web developers can use to prevent CSRF attacks. Some of the most effective techniques include using SameSite cookies and tokenization.

SameSite Cookies

SameSite cookies work by setting a flag on a cookie that tells the browser to only send that cookie with requests from the same origin. This means that if a user visits a malicious website that tries to submit a form to a different origin, such as a CSRF attack, the browser will not send the SameSite cookie with the request. This prevents the attacker from being able to authenticate as the user and perform actions on their behalf.

So, A cookie with the SameSite attribute set to "strict" or "lax" will not be sent with cross-site requests, such as those used in CSRF attacks.

Tokenization (CSRF token)

Tokenization works by generating a unique token for each user session and including that token in every form submission. When the server receives a form submission, it checks the submitted token against the expected value for the user session. If the tokens match, the server processes the request. If the tokens do not match, the server rejects the request.

Tokenization is effective because an attacker would need to know the user's unique token in order to perform a CSRF attack. Since each token is unique to the user session, an attacker would not be able to simply reuse a token from a previous session or generate a token that would be valid for a different user.

Let's have a look at the implementation of CSRF token.

We will start with the server-side implementation using Node.js with Express framework and csurf middleware.

In this example, we're using the csurf middleware to generate and verify CSRF tokens. The middleware sets a CSRF token cookie in the client's browser and adds a CSRF token to the request object in the csrfToken property.

The server exposes an API endpoint at /api/csrf-token that returns the CSRF token as a JSON object. This endpoint can be called by the client to retrieve the token.

The server also exposes an API endpoint at /api/create-post that expects a csrfToken parameter in the request body. The server checks that this token matches the CSRF token in the request headers (which was set automatically by the csurf middleware). If the tokens don't match, the server returns a 403 Forbidden error.

const express = require('express');
const csurf = require('csurf');
const cookieParser = require('cookie-parser');

const app = express();
const csrfProtection = csurf({ cookie: true });

app.use(cookieParser());
app.use(csrfProtection);

app.get('/api/csrf-token', (req, res) => {
  res.json({ csrfToken: req.csrfToken() });
});

app.post('/api/create-post', (req, res) => {
  // Verify CSRF token
  if (!req.csrfToken() || req.body.csrfToken !== req.csrfToken()) {
    return res.status(403).json({ message: 'Invalid CSRF token' });
  }

  // Process the request
  // ...
});

app.listen(3000, () => {
  console.log('Server listening on port 3000');
});

Now let's implement the CSRF token at Client Side.

In this example, we're using the axios library to make API requests to the server. The useEffect hook is used to fetch the CSRF token from the server and set it in the component's state.

When the user submits the form, we include the CSRF token in the request body. If the CSRF token is invalid, the server will return a 403 Forbidden error.

import axios from 'axios';
import { useState, useEffect } from 'react';

function App() {
  const [csrfToken, setCsrfToken] = useState(null);

  useEffect(() => {
    // Fetch CSRF token from the server
    async function fetchCsrfToken() {
      const { data } = await axios.get('/api/csrf-token');
      setCsrfToken(data.csrfToken);
    }

    fetchCsrfToken();
  }, []);

  async function handleSubmit(event) {
    event.preventDefault();

    // Submit the form with the CSRF token
    try {
      const { data } = await axios.post('/api/create-post', {
        title: 'Hello, world!',
        csrfToken,
      });

      console.log(data);
    } catch (error) {
      console.error(error);
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <button type="submit">Create Post</button>
    </form>
  );
}

How is the CSRF token different from JWT?

I have often seen people getting confused between CSRF and JWT tokens.

CSRF tokens and JSON Web Tokens (JWTs) are both types of tokens used in web application security, but they have different purposes and characteristics.

CSRF tokens are used to prevent CSRF attacks by verifying that a request is coming from a trusted source, while JWTs are used for authentication and authorization, and contain information about the identity and permissions of a user.

How to test for CSRF vulnerabilities in a web application?

There are several ways to test for CSRF vulnerabilities in a web application. Here are some techniques that can be used:

  1. Burp Suite: Burp Suite is a popular web application security testing tool that includes a CSRF PoC generator. This tool can be used to generate a CSRF PoC request that can be sent to the target web application to test for vulnerabilities.

    You can visit this link for more details - https://portswigger.net/support/using-burp-to-test-for-cross-site-request-forgery

  2. CSRFTester: CSRFTester is a dedicated tool designed to test for CSRF vulnerabilities. It automates the process of sending CSRF requests and can help identify vulnerabilities quickly.

  3. OWASP ZAP: OWASP ZAP is another popular web application security testing tool that includes a CSRF testing module. It can be used to test for CSRF vulnerabilities and generate reports that highlight any vulnerabilities found.

  4. Automated Scanners: There are many automated web application scanners available (such as OWASP ZAP, Acunetix, Burp Suite, Netsparker, and Qualys) that can be used to test for CSRF vulnerabilities. These scanners can save time and effort by automatically identifying potential vulnerabilities and generating reports.

It's important to note that while automated tools can help identify potential vulnerabilities, manual testing is still essential to validate the results and ensure that no false positives are reported. Additionally, it's important to use a combination of testing techniques to ensure that all potential attack vectors are identified and tested.

In conclusion, CSRF attacks can have serious consequences for both users and web applications. It's important to understand how these attacks work and to implement effective prevention techniques, such as using CSRF tokens and SameSite cookies. By taking proactive steps to prevent CSRF attacks, web developers can help protect their users' data and ensure the security and reliability of their applications. Ultimately, investing in security measures is essential to build trust with users and maintain the integrity of your web application.