阿里云主机折上折
  • 微信号
Current Site:Index > Common methods to defend against CSRF (such as Token verification)

Common methods to defend against CSRF (such as Token verification)

Author:Chuan Chen 阅读数:39254人阅读 分类: 前端安全

Common Methods to Defend Against CSRF (e.g., Token Verification)

CSRF (Cross-Site Request Forgery) is a common type of web attack where an attacker forges a user's identity to perform unintended actions. There are various methods to defend against CSRF, with Token Verification being one of the most commonly used. In addition to Token Verification, security can also be enhanced through methods like SameSite Cookies and Double Cookie Verification.

Principle of Token Verification

The core idea of Token Verification is to include a randomly generated token in the request, and the server verifies the legitimacy of the token to determine whether the request is trustworthy. The token is typically stored in the user's session or cookies and sent to the server when a form is submitted or an AJAX request is made.

The token generation and verification process is as follows:

  1. When a user visits a page, the server generates a random token and stores it in the session.
  2. The server embeds the token into the page form or returns it to the frontend.
  3. When the user submits the form or sends a request, the token is sent along with the request to the server.
  4. The server verifies whether the token matches the one stored in the session.
// Example: Generating a CSRF Token (Node.js + Express)
const express = require('express');
const csrf = require('csurf');
const cookieParser = require('cookie-parser');

const app = express();
app.use(cookieParser());
app.use(csrf({ cookie: true }));

app.get('/form', (req, res) => {
  res.send(`
    <form action="/process" method="POST">
      <input type="hidden" name="_csrf" value="${req.csrfToken()}">
      <button type="submit">Submit</button>
    </form>
  `);
});

app.post('/process', (req, res) => {
  // The middleware will automatically verify the CSRF Token
  res.send('Form processed successfully');
});

Methods of Storing and Transmitting Tokens

Tokens can be transmitted to the frontend in various ways, including:

  1. Hidden Form Fields: Embed the token as a hidden field in the form.

    <form action="/submit" method="POST">
      <input type="hidden" name="_csrf" value="RandomlyGeneratedToken">
      <!-- Other form fields -->
    </form>
    
  2. HTTP Request Headers: Transmit the token via headers in AJAX requests.

    fetch('/api/data', {
      method: 'POST',
      headers: {
        'X-CSRF-Token': 'RandomlyGeneratedToken'
      }
    });
    
  3. Cookie + Request Headers: Store the token in a cookie, and the frontend reads it and sends it via headers.

    // Read the token from the cookie
    const token = document.cookie.replace(/(?:(?:^|.*;\s*)XSRF-TOKEN\s*\=\s*([^;]*).*$|^.*$/, '$1');
    
    fetch('/api/data', {
      method: 'POST',
      headers: {
        'X-XSRF-Token': token
      }
    });
    

SameSite Cookie Attribute

SameSite is a cookie attribute used to restrict cross-site sending of cookies. It effectively prevents CSRF attacks, especially with widespread support in modern browsers.

SameSite has three possible values:

  • Strict: Cookies are only sent in same-site requests.
  • Lax: Cookies are partially allowed in cross-site requests (e.g., navigation jumps).
  • None: Cookies are sent in all requests (must be used with the Secure attribute).
// Setting a SameSite Cookie (Node.js)
res.cookie('sessionID', '12345', {
  httpOnly: true,
  secure: true,
  sameSite: 'Strict'
});

Double Cookie Verification

Double Cookie Verification is another method to defend against CSRF. The principle is to store the token in both the cookie and the request parameters, and the server verifies whether they match.

Implementation steps:

  1. When a user visits a page, the server sets a random token in a cookie.
  2. The frontend reads the token from the cookie and sends it as a parameter or header in the request.
  3. The server compares the token in the cookie with the token in the request.
// Example: Double Cookie Verification
app.get('/api/data', (req, res) => {
  const token = req.cookies['CSRF-TOKEN'];
  const requestToken = req.headers['x-csrf-token'];
  
  if (token && requestToken && token === requestToken) {
    res.send('Valid request');
  } else {
    res.status(403).send('Invalid CSRF Token');
  }
});

Other Defense Measures

In addition to the above methods, security can be further enhanced with the following measures:

  1. Verify Referer and Origin Headers: Check whether the request source is legitimate.

    app.use((req, res, next) => {
      const referer = req.get('Referer');
      if (referer && !referer.startsWith('https://yourdomain.com')) {
        return res.status(403).send('Invalid request source');
      }
      next();
    });
    
  2. Restrict HTTP Methods for Sensitive Operations: Ensure sensitive operations (e.g., modifying data) are only allowed via POST, PUT, DELETE, etc.

    app.post('/change-password', (req, res) => {
      // Handle password change logic
    });
    
  3. Short-Lived Tokens: Set a short expiration time for tokens to reduce the risk of exploitation.

    app.use(csrf({ cookie: { maxAge: 3600 } })); // Token expires after 1 hour
    

Considerations in Practical Applications

  1. Avoid Token Leaks: Tokens should not appear in URLs or logs to prevent interception.
  2. Adaptation for Frontend-Backend Separation Architectures: In SPAs (Single Page Applications), tokens can be obtained via an API and stored in memory.
    // Frontend fetches CSRF Token
    fetch('/csrf-token', { credentials: 'include' })
      .then(res => res.json())
      .then(data => {
        window.csrfToken = data.token;
      });
    
  3. Compatibility with Older Browsers: If using SameSite Cookies, consider compatibility issues with older browsers.

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.