CSRF and Clickjacking Defenses

intermediate
9 min

CSRF and Clickjacking Defenses

Not every web attack injects code. Some simply trick the browser, or the user, into sending requests they never intended. Cross-site request forgery (CSRF) and clickjacking are the classic examples, and both are defeated by headers and patterns you can add in an afternoon. This lesson covers how each works and the modern defense checklist.

Cross-Site Request Forgery (CSRF)

Browsers historically attached cookies to every request to a domain, no matter which site initiated the request. CSRF abuses this: a malicious page causes the victim's browser to submit a request, for example a hidden auto-submitting form, to your site, and the browser helpfully includes the victim's session cookie. If your endpoint only checks the cookie, the forged request looks completely legitimate. Classic targets: changing an email address, transferring funds, deleting data. The attacker never sees the response; they only need the state change to happen.

Defense 1: SameSite cookies

The SameSite cookie attribute controls whether cookies accompany cross-site requests:

  • SameSite=Lax (the modern browser default): cookies are sent on top-level navigations but not on cross-site POSTs, killing the classic attack.
  • SameSite=Strict: never sent cross-site, strongest but can log users out of legitimate deep links.
javascript
// Express session cookie hardened against CSRF and theft app.use(session({ secret: process.env.SESSION_SECRET, cookie: { httpOnly: true, // no script access secure: true, // HTTPS only sameSite: 'lax', // not sent on cross-site POSTs }, }));

Do not rely on browser defaults; set the attribute explicitly.

Defense 2: CSRF tokens

The synchronizer token pattern embeds a random per-session token in every form and requires it back on submission. A cross-site attacker cannot read your pages (same-origin policy), so they cannot obtain the token, and their forged request fails validation. Most frameworks ship this: Django and Rails enable it by default; Express has middleware; check the box rather than reinventing it. For APIs consumed by JavaScript, a common equivalent is the double-submit cookie or a custom header requirement, since cross-site forms cannot set custom headers.

Defense 3: Verify origin and design safely

  • Check the Origin or Sec-Fetch-Site request headers server-side and reject unexpected cross-site state changes.
  • Keep GET requests free of side effects; anything that changes state should require POST, PUT or DELETE. A GET /delete?id=5 link is a CSRF gift.
  • Require re-authentication or a fresh MFA prompt for the most sensitive actions.

Clickjacking

Clickjacking loads your site in an invisible iframe layered over a decoy page. The victim believes they are clicking 'Play video' but is actually clicking your 'Confirm transfer' button positioned exactly beneath the cursor. No code is injected; the user genuinely clicked your real button.

Defense: refuse to be framed

Two headers control framing:

# Modern, flexible (part of CSP) Content-Security-Policy: frame-ancestors 'none' # Legacy but still widely respected X-Frame-Options: DENY

frame-ancestors 'none' forbids all framing; use 'self' if your own site legitimately embeds its pages. Send both headers for maximum browser coverage. If you offer an embeddable widget, allowlist specific partner origins instead of leaving framing open, and keep sensitive account pages framed by nobody.

Putting It Together

A pragmatic checklist for every session-based application:

  1. Session cookies: HttpOnly; Secure; SameSite=Lax at minimum.
  2. CSRF tokens on all state-changing forms, via your framework's built-in support.
  3. State changes never on GET.
  4. frame-ancestors 'none' plus X-Frame-Options: DENY unless you have a documented embedding need.
  5. Step-up authentication on high-impact actions.

These defenses are cheap, mostly declarative, and eliminate two entire attack classes.

Key Takeaways

  • CSRF forges state-changing requests by riding the victim's cookies; the server sees a 'legitimate' request.
  • SameSite cookies, CSRF tokens and origin checks together shut it down.
  • Clickjacking overlays your real UI in an invisible iframe to steal clicks.
  • frame-ancestors and X-Frame-Options stop your pages from being framed.
  • Keep GET side-effect free and re-authenticate sensitive operations.

Next lesson: The OWASP Top 10 Explained, the industry's map of the most critical web application risks.

CSRF and Clickjacking Defenses - Cyber Security | CodeYourCraft | CodeYourCraft