Exercises
Drills to test your CSRF instincts: each scenario describes an endpoint and asks what defence applies. Use them in code review prep or new-hire onboarding — the goal is "the right defence comes to mind before you check the spec".
Eight CSRF scenarios with the right answer
EXAMPLE
# ===== Drill 1 — Server-rendered Blade app, cookie session =====
# Endpoint: POST /account/email (changes the logged-in user's email)
# What is the minimum defence?
#
# ANSWER:
# - 'web' middleware group with VerifyCsrfToken
# - @csrf hidden field in the form
# - SameSite=Lax + Secure on the session cookie
# - Origin header check is OPTIONAL belt-and-braces
# WHY: cookie-auth + state change = classic CSRF target
# ===== Drill 2 — REST API, Authorization: Bearer JWT, no cookies =====
# Endpoint: POST /api/orders
#
# ANSWER:
# - NO CSRF token needed; browsers cannot attach a custom Authorization
# header on cross-origin form submissions
# - Still require: rate limiting, audience claim check, expiry check
# WHY: the bearer-token requirement IS the CSRF defence
# ===== Drill 3 — SPA + cookie session, calls the same-origin API =====
# Endpoints: SPA on example.com, API on api.example.com (subdomain)
#
# ANSWER:
# - Double-submit token: server sets XSRF-TOKEN cookie, SPA echoes it
# as X-XSRF-TOKEN header (Angular and Laravel default this in)
# - SameSite=Lax on the session cookie
# - CORS allowed_origins = exact list, supports_credentials = true
# WHY: still cookie-auth; XHR can attach the header but a forged form cannot
# ===== Drill 4 — A 'view' GET that silently logs the user out =====
# Endpoint: GET /logout (kills the session)
#
# ANSWER: change it to POST.
# WHY: GET requests can be triggered by an <img>, an embedded <iframe>,
# or a link from any other site. SameSite=Lax does NOT block GETs
# for top-level navigations. State change on GET is the bug.
# ===== Drill 5 — Webhook endpoint receiving signed POSTs from Stripe =====
# Endpoint: POST /webhooks/stripe
#
# ANSWER:
# - EXEMPT from CSRF middleware (no cookie auth involved)
# - Verify the signature header (Stripe-Signature) with constant-time compare
# - Validate the timestamp window (< 5 minutes old)
# WHY: the signature IS the auth; CSRF token would be wrong (Stripe does
# not have one) and a missing signature check is the real risk.
# ===== Drill 6 — Admin panel behind an Office VPN =====
# Endpoint: POST /admin/users/{id}/delete
#
# ANSWER: defence in depth.
# - CSRF token (web middleware) — the VPN does NOT prevent CSRF
# - SameSite=Strict on admin cookies (not just Lax)
# - Optionally, mTLS for admin routes (see csrf/certificate lesson)
# WHY: 'we are on a VPN' protects nothing if an admin happens to also
# have a browser tab open on a hostile site
# ===== Drill 7 — Multi-step form with file upload =====
# Endpoint: POST /listings (multipart/form-data, photos + text)
#
# ANSWER: CSRF token works exactly the same; embed it in the form as a
# hidden field. The framework handles it.
# WHY: form encoding does not change the threat model — the browser
# attaches the session cookie either way.
# ===== Drill 8 — Public form anyone can submit (no auth) =====
# Endpoint: POST /contact (sends an email to support)
#
# ANSWER:
# - No CSRF (nothing to forge — there is no authenticated user)
# - Defend against ABUSE instead: reCAPTCHA, rate limit, email
# content sanitisation, no rendering of user HTML in the email
# WHY: CSRF is about hijacking authority. No authority -> no CSRF.
# ===== Scoring =====
# 8 / 8 -> you can run a CSRF review on a PR in 5 minutes
# 6 / 8 -> bookmark the csrf/summary lesson
# < 6 -> sit down with the framework docs of the auth model you use
Why it matters
CSRF defence comes down to two questions: "is the auth attached automatically by the browser?" (cookie -> yes, bearer header -> no) and "does this endpoint change state?". Yes + yes = require a token. Yes + no = harmless. No + yes = defend against abuse, but not CSRF. Memorise that decision matrix and you will answer almost every review question correctly.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Discussion
Loading…