CSRF Attack: Cross-Site Request Forgery

CSRF (Cross-Site Request Forgery) is a web security vulnerability where an attacker tricks a user into making unintended requests to a trusted website where they are authenticated. Protection methods include CSRF tokens, SameSite cookies, and proper request validation.

CSRF: Cross-Site Request Forgery Protection

Cross-Site Request Forgery (CSRF) is a type of security vulnerability that tricks a user into performing unintended actions on a web application where they are authenticated. Also known as "one-click attack" or "session riding," CSRF exploits the trust that a website has in a user's browser. When a user is logged into a website, their browser automatically sends session cookies with every request. A CSRF attack crafts malicious requests that use this existing authentication to perform actions without the user's knowledge or consent.

Understanding CSRF is essential for building secure web applications. It is one of the most common web vulnerabilities and has been included in the OWASP Top 10 for years. To understand CSRF properly, it is helpful to be familiar with concepts like HTTP protocol, session, cookies and authentication, and web security fundamentals.

What Is CSRF

CSRF is an attack that forces an authenticated user to execute unwanted actions on a web application. The attacker tricks the user's browser into making a request to a vulnerable website where the user is already logged in. Because the browser automatically includes the user's session cookies, the target website processes the request as if it came from the legitimate user.

  • Cross-Site: The attack originates from a different website than the target application.
  • Request Forgery: The attacker forges a request that appears to come from the legitimate user.
  • Trust Exploitation: The attack exploits the website's trust in the user's authenticated session.
  • State-Changing Actions: CSRF typically targets actions that change state (password changes, fund transfers, account deletions).
CSRF attack flow:
1. User logs into bank.com and stays logged in
   (Session cookie stored in browser)

2. User visits attacker.com (malicious site)

3. attacker.com contains hidden form or script:
   <img src="https://bank.com/transfer?amount=1000&to=attacker">

4. Browser automatically includes session cookie with request

5. bank.com processes the request as legitimate
   (Money transferred without user consent)

How CSRF Works

CSRF attacks rely on three key conditions to succeed. Understanding these conditions helps you understand how to defend against them.

  1. Authentication Persistence: The user is logged into the target website, and the session cookie remains valid.
  2. Predictable Request Structure: The target application uses predictable URLs and parameters for state-changing operations.
  3. Automatic Credential Submission: Browsers automatically include cookies with requests to the domain where they were set.
Example vulnerable request:
GET /change-email?email=attacker@example.com HTTP/1.1
Host: vulnerable-site.com
Cookie: session=abc123

# The attacker can embed this in an image tag:
<img src="https://vulnerable-site.com/change-email?email=attacker@example.com">

# When the user visits the attacker's site, the request is made automatically
# with the session cookie included, and the email address is changed.

Real-World CSRF Attack Examples

CSRF attacks can target any state-changing operation. Understanding real attack scenarios helps you identify vulnerable areas in your applications.

  • Bank Transfer: Attacker crafts a request to transfer funds from the victim's account to their own account.
  • Password Change: Attacker changes the victim's password to lock them out of their account.
  • Email Change: Attacker changes the account email address to take over the account via password reset.
  • Account Deletion: Attacker triggers account deletion, causing permanent data loss.
  • Privilege Escalation: Attacker adds themselves as an administrator to a system.
  • API Actions: Attacker triggers API actions that modify data or settings.
Hidden form CSRF attack example:
<!-- Malicious page on attacker.com -->
<html>
<body>
    <h1>Click here for free gift!</h1>
    
    <form id="csrf-form" action="https://bank.com/transfer" method="POST">
        <input type="hidden" name="amount" value="1000">
        <input type="hidden" name="to_account" value="attacker123">
    </form>
    
    <script>
        // Auto-submit the form when page loads
        document.getElementById('csrf-form').submit();
    </script>
</body>
</html>

CSRF vs XSS: Understanding the Difference

CSRF and Cross-Site Scripting (XSS) are often confused, but they are fundamentally different vulnerabilities with different attack vectors and defense strategies.

Feature CSRF XSS
Attack Vector如果 Exploits the website's trust in the user's browser如果 Exploits the user's trust in the website如果
Target如果 State-changing actions (transfers, password changes)如果 Stealing data, session hijacking, defacement如果
Execution如果 adaghanAttacker forces browser to make unauthorized requests如果 Attacker injects malicious scripts into the website如果
Defense如果 CSRF tokens, SameSite cookies, referer validation如果 Input validation, output encoding, CSP如果

CSRF Protection Methods

Several effective methods exist to protect applications against CSRF attacks. The most common and reliable approach is using anti-CSRF tokens.

CSRF Tokens (Synchronizer Token Pattern)

The synchronizer token pattern is the most widely used CSRF defense. A unique, unpredictable token is generated for each user session and included in forms. When the form is submitted, the server validates that the token matches the one stored in the session.

CSRF token implementation (PHP example):
<?php
// Generate token and store in session
session_start();
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
$token = $_SESSION['csrf_token'];
?>

<!-- Include token in form -->
<form method="POST" action="/transfer">
    <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($token) ?>">
    <input type="text" name="amount">
    <input type="text" name="to_account">
    <button type="submit">Transfer</button>
</form>

<?php
// Validate token on submission
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
        die('CSRF validation failed');
    }
    // Process the request
}
?>

SameSite Cookies

The SameSite cookie attribute prevents browsers from sending cookies with cross-site requests. This is a powerful defense that works at the browser level. Setting `SameSite=Strict` or `SameSite=Lax` can block CSRF attacks entirely.

SameSite cookie configuration:
// Set SameSite attribute when creating cookies
Set-Cookie: session=abc123; SameSite=Strict; Secure; HttpOnly

// In PHP
session_set_cookie_params([
    'lifetime' => 3600,
    'path' => '/',
    'domain' => 'example.com',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Strict'
]);

// SameSite options:
// Strict - Cookie only sent for same-site requests (most secure)
// Lax - Cookie sent for top-level navigation and same-site requests
// None - Cookie sent for all requests (requires Secure flag)

Custom Request Headers

Many modern applications use custom headers for API requests. Since browsers do not allow cross-site requests to set custom headers without CORS, this provides built-in CSRF protection for API endpoints.

Custom header protection (JavaScript example):
// Client-side: Include custom header in requests
fetch('/api/transfer', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-Requested-With': 'XMLHttpRequest',
        'X-CSRF-Token': csrfToken
    },
    body: JSON.stringify({ amount: 1000, to: 'account123' })
});

// Server-side: Validate header
if ($_SERVER['HTTP_X_REQUESTED_WITH'] !== 'XMLHttpRequest') {
    die('Invalid request origin');
}

Double Submit Cookie Pattern

In this pattern, the CSRF token is sent both as a cookie and as a request parameter. The server validates that both values match. This works well for stateless APIs.

Double submit cookie pattern:
// Set CSRF token as cookie
document.cookie = "csrf_token=abc123; SameSite=Lax; Secure";

// Include token in request
fetch('/api/transfer', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-CSRF-Token': getCookie('csrf_token')
    },
    body: JSON.stringify({ amount: 1000 })
});

// Server validates token matches
function validateCSRF() {
    $cookieToken = $_COOKIE['csrf_token'] ?? '';
    $headerToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
    return hash_equals($cookieToken, $headerToken);
}

CSRF Protection in Popular Frameworks

Modern web frameworks include built-in CSRF protection that handles token generation and validation automatically.

Framework-specific CSRF protection:
// Laravel (PHP)
// CSRF token automatically added to forms
<form method="POST">
    @csrf
    <input type="text" name="amount">
</form>

// Django (Python)
// CSRF token included with {% csrf_token %}
<form method="post">
    {% csrf_token %}
    <input type="text" name="amount">
</form>

// Ruby on Rails
// CSRF protection enabled by default in ApplicationController
// Tokens automatically included in forms
<%= form_tag('/transfer', method: :post) do %>
    <%= text_field_tag :amount %>
    <%= submit_tag "Transfer" %>
<% end %>

// Express.js (Node.js)
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });

app.post('/transfer', csrfProtection, (req, res) => {
    // CSRF token automatically validated
});

CSRF Protection Checklist

Use this checklist to ensure your application is properly protected against CSRF attacks.

  • Use CSRF tokens for all state-changing operations: Include unique, unpredictable tokens in forms and API requests.
  • Validate tokens on the server: Always verify tokens match before processing requests.
  • Set SameSite cookie attribute: Use SameSite=Strict or Lax for session cookies.
  • Use HTTPS everywhere: CSRF tokens should only be transmitted over encrypted connections.
  • Validate origin and referer headers: Check that requests originate from your domain.
  • Do not use GET requests for state-changing operations: Always use POST, PUT, PATCH, or DELETE.
  • Regenerate tokens after authentication: Create new tokens after login to prevent session fixation.
  • Use framework-provided protection: Leverage built-in CSRF protection in your framework.
  • Implement proper CORS configuration: Restrict which origins can access your API.

Common CSRF Vulnerabilities

Even with CSRF protection in place, developers can make mistakes that leave applications vulnerable. Being aware of these common issues helps you avoid them.

  • Missing CSRF Protection for API Endpoints: APIs often lack CSRF protection because they are designed for machine-to-machine communication.
  • Using GET for State-Changing Operations: GET requests should be idempotent and safe. Using GET for actions like deletion is a major CSRF risk.
  • Predictable CSRF Tokens: Tokens that are not cryptographically random can be guessed by attackers.
  • Token Not Tied to Session: Tokens that are not bound to the user session can be reused across sessions.
  • Token Validation Timing: Failing to validate tokens before processing requests.
  • SameSite Cookie Not Configured: Not setting SameSite attribute on session cookies leaves browsers vulnerable.
Vulnerable code example (what NOT to do):
// Vulnerable: GET request for state-changing action
<a href="/delete-account?user=123">Delete Account</a>

// Vulnerable: Missing CSRF token validation
if ($_POST['action'] === 'transfer') {
    // Process transfer without validating token
    processTransfer($_POST['amount'], $_POST['to']);
}

// Vulnerable: Predictable token
$_SESSION['csrf_token'] = md5($_SESSION['user_id']);  // Too predictable!

// Vulnerable: No token regeneration after login
// Token persists across sessions, allowing session fixation

Testing for CSRF Vulnerabilities

Regular testing helps identify CSRF vulnerabilities before attackers can exploit them. Several tools and techniques can help with CSRF testing.

  • Manual Testing: Try to submit requests from a different origin using tools like Postman or curl without including the CSRF token.
  • Automated Scanners: Use tools like OWASP ZAP, Burp Suite, or Nikto to scan for CSRF vulnerabilities.
  • Code Review: Examine code for missing CSRF protection on state-changing endpoints.
  • Security Headers Check: Verify that SameSite cookie attributes are properly configured.
  • Penetration Testing: Engage security professionals to test your application's defenses.
Simple CSRF test using curl:
# Attempt to submit form without CSRF token
curl -X POST https://example.com/transfer \
    -H "Cookie: session=abc123" \
    -d "amount=1000&to=attacker"

# If the request succeeds, the endpoint is vulnerable
# If it fails with CSRF error, protection is working

CSRF in Single Page Applications (SPAs)

Single Page Applications present unique challenges for CSRF protection because they often use APIs rather than traditional forms. However, several strategies work well for SPAs.

  • Cookie-Based Tokens: Set CSRF token as a cookie and require it in request headers (double submit pattern).
  • SameSite Cookies: Use SameSite=Strict or Lax on session cookies to prevent cross-site requests.
  • Custom Headers: Require custom headers like X-Requested-With or X-CSRF-Token that browsers do not send cross-origin by default.
  • Origin Validation: Validate the Origin and Referer headers on the server.
  • Token in Meta Tag: Store CSRF token in meta tags and include in all API requests.
SPA CSRF protection (React example):
// Add CSRF token to index.html
<meta name="csrf-token" content="{{ csrf_token() }}">

// Axios interceptor to add token to all requests
import axios from 'axios';

const csrfToken = document.querySelector('meta[name="csrf-token"]').content;

axios.defaults.headers.common['X-CSRF-Token'] = csrfToken;

// Or with fetch
fetch('/api/transfer', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-CSRF-Token': csrfToken
    },
    body: JSON.stringify(data)
});

Frequently Asked Questions

  1. Is CSRF still a problem with SameSite cookies?
    SameSite cookies provide excellent CSRF protection, but not all browsers support them, and older browsers remain in use. A defense-in-depth approach combining SameSite cookies with CSRF tokens is recommended for maximum protection.
  2. Do I need CSRF protection for GET requests?
    GET requests should be idempotent and safe by design. If GET requests perform state-changing operations, they are violating HTTP standards and are vulnerable to CSRF. Use POST, PUT, PATCH, or DELETE for state-changing actions and protect them with CSRF tokens.
  3. What is the difference between CSRF and session hijacking?
    Session hijacking steals the session cookie to impersonate the user. CSRF tricks the user's browser into making requests using the existing session cookie. CSRF does not steal the cookie; it exploits the browser's automatic cookie submission behavior.
  4. Can CSRF attacks work with JSON APIs?
    Yes. While traditional CSRF uses HTML forms, JSON APIs can be vulnerable if they accept requests with content types that browsers can send cross-origin (like application/x-www-form-urlencoded). Modern defenses include custom headers, CSRF tokens, and proper CORS configuration.
  5. What is the most effective CSRF protection?
    A defense-in-depth approach is most effective. Combine CSRF tokens for all state-changing requests, SameSite=Strict or Lax for session cookies, and validate Origin/Referer headers. Use built-in framework protection whenever possible.
  6. What should I learn next after understanding CSRF?
    After mastering CSRF protection, explore related security topics like XSS, SQL injection, Content Security Policy (CSP), and security headers. Also study Authentication vs Authorization for comprehensive web security.

Conclusion

Cross-Site Request Forgery remains a critical web security vulnerability that every developer must understand and defend against. By exploiting the browser's automatic inclusion of session cookies, CSRF attacks can trick authenticated users into performing unintended actions. The consequences can range from unauthorized fund transfers to complete account takeover.

The most effective defense against CSRF is a layered approach. Use anti-CSRF tokens for all state-changing requests, configure session cookies with the SameSite attribute, validate origin headers, and leverage the built-in protection provided by modern web frameworks. Never rely on a single defense mechanism when multiple layers can be implemented.

As web applications become more complex with APIs, SPAs, and microservices, CSRF protection must evolve accordingly. Understanding the principles behind CSRF helps you apply appropriate protection regardless of your architecture. Regular testing, code reviews, and staying informed about emerging threats are essential practices for maintaining strong CSRF defenses.

To deepen your security knowledge, explore related topics like XSS for protecting against script injection, SQL injection prevention for database security, Content Security Policy (CSP) for defense in depth, and Authentication vs Authorization for securing user sessions. Together, these skills form a complete foundation for building secure web applications.