CWE-352 / OWASP A01

Cross-Site Request Forgery (CSRF, CWE-352): Cookie Hijacking & Modern Token Defenses

How cross-site requests exploit browser cookie ambient authority to execute unauthorized actions, and why SameSite=Lax alone is insufficient without anti-forgery tokens.

💡 Plain English Explainer (ELI5)

Imagine you walk into your bank and log in. While keeping that tab open, you open a prank email that secretly loads an image tag: ``. Because your browser automatically attaches your login cookies to every request heading toward your bank, the bank processes the wire transfer assuming YOU wanted it.

Core Concepts & Key Terms

Ambient Authority
The browser's automatic behavior of attaching credentials (cookies, HTTP basic auth) to every outgoing request matching the target domain, regardless of which website originated it.
SameSite Cookie Attribute
A directive telling browsers whether to send cookies on cross-site requests (`Strict`, `Lax`, or `None`).
Anti-CSRF Synchronizer Token
A cryptographically unpredictable token generated per session that the server requires in form submissions or custom request headers.
Double-Submit Cookie Pattern
A stateless defense where the client reads a random value from a cookie and mirrors it inside an `X-CSRF-Token` HTTP header.

Step-by-Step Attack Flow

Step 1

1. Authenticated User Session

A victim authenticates with app.com. The server issues a session cookie without strict SameSite protection.

Step 2

2. Visiting Malicious Origin

The victim visits an attacker-controlled website evil.com containing an invisible auto-submitting HTML form targeting app.com/api/settings/email.

Step 3

3. Browser Submits with Cookies

The victim's browser sends a POST request to app.com. Ambient authority attaches the victim's session cookie.

Step 4

4. Unauthorized State Change

Because the server only checked cookie validity and lacked an anti-CSRF token, the victim's email or password is changed.

Source Code: Flaw vs. Secure Implementation

VULNERABLE PATTERN
# VULNERABLE: State-Changing Endpoint Relying Strictly on Session Cookies
from flask import Flask, request, session, jsonify

app = Flask(__name__)

@app.route("/api/user/email", methods=["POST"])
def update_email():
    # CRITICAL: Authenticates purely via ambient session cookie
    # Vulnerable to cross-site POST forms from third-party websites!
    user_id = session.get("user_id")
    if not user_id:
        return jsonify({"error": "Unauthorized"}), 401
        
    new_email = request.form.get("email")
    db.execute("UPDATE users SET email = ? WHERE id = ?", (new_email, user_id))
    return jsonify({"status": "success", "email": new_email})
HARDENED DEFENSE
# SECURE: Anti-CSRF Token Validation + SameSite=Strict Cookie
import secrets
import hmac
from flask import Flask, request, session, abort, jsonify

app = Flask(__name__)
app.config.update(
    SESSION_COOKIE_SECURE=True,
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SAMESITE="Strict" # Prevents cross-site cookie attachment
)

@app.before_request
def csrf_protect():
    if request.method in ("POST", "PUT", "DELETE", "PATCH"):
        token = request.headers.get("X-CSRF-Token") or request.form.get("csrf_token")
        expected = session.get("csrf_token")
        # Enforce constant-time comparison of anti-forgery token
        if not expected or not token or not hmac.compare_digest(expected, token):
            abort(403, "CSRF Token Validation Failed")

@app.route("/api/user/email", methods=["POST"])
def update_email():
    user_id = session["user_id"]
    new_email = request.form["email"]
    db.execute("UPDATE users SET email = ? WHERE id = ?", (new_email, user_id))
    return jsonify({"status": "success"})

Engineering Hardening Checklist

← Browse Full Security Directory Explore Vulnerability Playbooks →