blog.dopana

Back

CSRF stands for Cross-Site Request Forgery. While it sounds academic and complex, the core vulnerability exploits a simple human behavior: tricking your web browser into executing an unintended action on a trusted website without your awareness.

1. Explain Like I’m 10 (ELI5)#

Imagine you just entered an exclusive VIP members club (such as your online bank or social media profile):

  1. When you check in at the reception desk, they stamp a VIP pass on your hand (this is your browser’s Session Cookie).
  2. As long as the stamp is visible, the club staff knows you are a valid member and honors all your requests without asking for your ID again.
  3. Later, you walk outside and visit a shady street market stall (evil.com).
  4. The merchant writes an order on paper: “Transfer $1,000 from this member’s account to me”, grabs your hand with the VIP stamp, presses it onto the paper, and delivers it to the VIP club.
  5. The club sees a legitimate VIP stamp \rightarrow assumes you requested the transfer \rightarrow and debits your money immediately!

You never intended to transfer money, but your browser was tricked into providing its credentials. That is CSRF.

flowchart LR
    subgraph AttackerSite["Malicious Website (evil.com)"]
        MaliciousScript["Hidden Script / Auto-submit Form<br/>POST https://bank.com/transfer"]
    end

    subgraph UserBrowser["User Browser (Client)"]
        Victim["Logged-in User<br/>(Holds Valid Session Cookie)"]
    end

    subgraph BankServer["Target Bank Server (bank.com)"]
        Backend["Validates Cookie<br/>-> Executes Transfer!"]
    end

    Victim -->|1. Visits malicious site| MaliciousScript
    MaliciousScript -->|2. Triggers silent cross-site request| Victim
    Victim -->|3. Automatically attaches Cookie + Sends request| Backend

2. The Two Pillars of a CSRF Vulnerability#

CSRF does not require an attacker to steal your password or decrypt session secrets. Instead, it exploits ambient trust between web browsers and servers:

  1. Automatic Cookie Transmission (Ambient Credentials): Whenever your browser sends a request to any destination domain (e.g., bank.com), it automatically attaches all stored cookies matching that domain, regardless of which website triggered the request.
  2. Naive Server Authentication: The server only checks “Is this user authenticated with a valid session cookie?” without verifying “Did this request genuinely originate from our own application interface?”.

3. Key Differences: CSRF vs. XSS#

Developers often confuse CSRF with XSS (Cross-Site Scripting). Here is how they compare:

DimensionXSS (Cross-Site Scripting)CSRF (Cross-Site Request Forgery)
MechanismInjects and executes malicious JavaScript within the victim’s page contextTricks the browser into sending unauthorized requests from an external origin
Steals Cookies?Can read and exfiltrate cookies (unless protected with HttpOnly)Cannot read or steal cookies (simply rides on existing sessions)
Root CauseUnsanitized / unescaped user inputs rendered in the DOMThe browser’s automatic cross-site credential attachment behavior

[!NOTE] XSS steals your identity; CSRF borrows your authority without knowing your secret tokens or passwords.

4. Common Attack Vectors#

Scenario 1: Unprotected GET Requests via <img> Tags#

If a web application mutates state using HTTP GET (violating REST conventions):

evil.com/page.html
<!-- The victim's browser silently fetches the URL and attaches session cookies -->
<img src="https://bank.com/api/transfer?to=attacker&amount=5000" width="0" height="0" />
html

Scenario 2: Hidden Auto-Submitting POST Forms#

Even with HTTP POST, attackers can embed hidden forms and submit them programmatically using JavaScript:

evil.com/exploit.html
<form id="csrfForm" action="https://social.com/api/delete-account" method="POST">
  <input type="hidden" name="confirm" value="true" />
</form>

<script>
  // Submits automatically upon page load
  document.getElementById('csrfForm').submit();
</script>
html

5. Comprehensive Defense Strategies#

1. CSRF Tokens (Synchronizer Token Pattern)#

The gold standard for form-based web applications:

  • The server generates a unique, cryptographically random token tied to the user’s current session.
  • When rendering a form, the token is embedded as a hidden field or header.
  • Because malicious third-party websites are restricted by the Same-Origin Policy (SOP), they cannot read the CSRF token from your application, causing forged requests to be rejected.
transfer.html
<form action="/transfer" method="POST">
  <!-- Secret CSRF Token generated by server -->
  <input type="hidden" name="_csrf" value="d9a8f7b2c3e4..." />
  <input type="number" name="amount" />
  <button type="submit">Transfer Funds</button>
</form>
html

The SameSite cookie attribute instructs browsers when to send cookies with cross-site requests:

  • SameSite=Strict: Cookies are strictly omitted in all cross-site requests (maximum security, but users clicking external links must log in again).
  • SameSite=Lax (Default in modern browsers): Cookies are blocked on cross-site state-modifying requests (like POST or embedded images), but allowed during safe top-level navigations (like regular links).
  • SameSite=None: Cookies are sent in all cross-site contexts (requires the Secure flag over HTTPS).
Set-Cookie: sessionId=xyz123; Path=/; Secure; HttpOnly; SameSite=Lax
http

[!TIP] Setting SameSite=Lax or SameSite=Strict alongside Secure and HttpOnly flags neutralizes the vast majority of traditional CSRF attack vectors.

In architectures where servers do not maintain server-side session stores:

  1. The server issues a random token into a readable cookie (e.g., XSRF-TOKEN).
  2. The Single Page Application (React, Vue, Svelte) reads the cookie via JavaScript and attaches it to a custom HTTP header (e.g., X-XSRF-TOKEN).
  3. The server compares the header token with the cookie token. If both match, the request is deemed authentic.

4. Validating Origin, Referer, and Sec-Fetch-Site Headers#

On backend API gateways, verify that incoming mutating requests match expected hostnames:

middleware/csrfCheck.js
export function verifyOrigin(req, res, next) {
  const origin = req.headers['origin'] || req.headers['referer'];
  const allowedHost = 'https://mybank.com';

  if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(req.method)) {
    if (!origin || !origin.startsWith(allowedHost)) {
      return res.status(403).json({ error: 'CSRF Protection: Invalid Request Origin' });
    }
  }
  next();
}
javascript

6. CSRF in Modern SPA & JWT Architectures#

In modern frontend architectures:

  • If JWT tokens are stored in JavaScript memory and passed via the Authorization: Bearer <token> header, CSRF is eliminated, because browsers never automatically inject custom Authorization headers on cross-site requests.
  • However, if you store JWTs inside Cookies for convenience, your application remains fully vulnerable to CSRF unless proper CSRF defenses (SameSite, Anti-CSRF headers) are enforced.

7. Summary#

CSRF is a reminder that blind trust in ambient credentials creates severe security liabilities.

To safeguard your web applications:

  1. Use POST, PUT, or DELETE for all state-changing actions.
  2. Configure SameSite=Lax or SameSite=Strict on all authentication cookies.
  3. Require Anti-CSRF Tokens or custom headers for sensitive mutations.
  4. Validate Origin and Sec-Fetch-Site headers at the server middleware layer.

References#