Stop 13 · SecurityTopics · 4/12

CSRF Protection

Part of the Backend Developer Roadmap

In short

Your browser sends cookies automatically, even when another website triggers the request. CSRF abuses that politeness to make logged-in users do things they never clicked.

Cross-Site Request Forgery — preventing a malicious site from tricking a logged-in user's browser into making unwanted requests to your API using their existing session/cookies.

The blob as a gate watchman stops a parcel and checks a golden token card taped to it.

Think of it like this

The watchman who trusts the flat number

Your society watchman lets in any parcel that says 'For flat 302', because 302 is a known resident. A prankster books twenty fake deliveries to your flat. Now the watchman asks for a secret code that only you could have given. No code, no entry.

Watch it happen

A forged request from another site

You (browser)
evil.example
bank.example

login, gets cookie

1/5You log in to your bank. The browser stores a session cookie for bank.example.

The big ideas

What you need to know

Cookies are the weak spot

CSRF only works when auth rides along automatically, as with cookies. A Bearer token you add in JS isn't sent by other sites.

SameSite cookies

SameSite=Lax or Strict tells the browser not to send the cookie on cross-site POSTs. Modern browsers default to Lax.

CSRF tokens

Put a random token in your forms and check it on the server. Other sites can't read your page, so they can't copy it.

GET must not change things

Never delete or transfer on GET. Links and images can fire GET requests from anywhere, and Lax still allows top-level GETs.

See it in code

Walk through the code

ts
1import crypto from "node:crypto";2 3app.get("/transfer", (req, res) => {4  req.session.csrf = crypto.randomBytes(32).toString("hex");5  res.render("transfer", { csrf: req.session.csrf });6});7 8app.post("/transfer", (req, res) => {9  if (!req.session.csrf || req.body._csrf !== req.session.csrf) {10    return res.status(403).send("Bad CSRF token");11  }12  // safe to move money now13});

1/2When the real form is shown, the server makes a random token, saves it in the session and puts it in the form.

The form includes a hidden _csrf input with the token. Set session cookies with SameSite=Lax, HttpOnly and Secure as well; layers beat a single lock.

Save yourself the pain

Common mistakes

Oops

Assuming a JSON API with cookie auth can't be hit by CSRF.

Do this instead

If auth is a cookie, think about CSRF. Use SameSite cookies, check the Origin header, or require a token header.

Oops

Setting SameSite=None on the session cookie to fix a login bug.

Do this instead

None removes the protection. Only use it with Secure, and only when a cross-site setup truly needs it.

Your mission

Forge a request against yourself

~30 min

Quick check

Your SPA sends the JWT in an Authorization header set by JavaScript, with no cookies. How exposed is it to CSRF?

Go deeper

  1. 1Learn why CSRF specifically targets cookie-based session auth, not token-based auth in headers.
  2. 2Implement CSRF tokens for a cookie-authenticated form submission.
  3. 3Understand `SameSite` cookie attributes as a modern, simpler mitigation.

More topics at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge