DEV Community

Cover image for CSRF Protection in React: Tokens, Cookies & Best Practices
Pentest Testing Corp
Pentest Testing Corp

Posted on

CSRF Protection in React: Tokens, Cookies & Best Practices

Why CSRF still matters in React apps

Even with SPAs, state-changing requests (POST/PUT/DELETE) can be forged if your session cookie is sent automatically. Use an explicit CSRF defense so the browser must prove intent.

Core strategy (TL;DR)

  1. Issue a per-session (or per-request) CSRF token from your API.
  2. Store it in an HttpOnly cookie or expose via a safe bootstrap endpoint.
  3. Frontend must send the token in a custom header (e.g., X-CSRF-Token).
  4. Backend validates header token === server copy (or double-submit cookie).
  5. Harden cookies with SameSite, Secure, HttpOnly.

CSRF Protection in React: Tokens, Cookies & Best Practices


Backend: minimal Express CSRF token endpoint

// server/csrf.js
import express from "express";
import crypto from "crypto";
const app = express();

app.use(express.json());

// naive in-memory store; replace with signed JWT or DB per user/session
const tokens = new Map();

app.get("/csrf-token", (req, res) => {
  const sid = (req.headers["x-session-id"] || crypto.randomUUID());
  const token = crypto.randomBytes(32).toString("hex");
  tokens.set(sid, token);

  // set session + double-submit cookie
  res.setHeader("Set-Cookie",
    `sid=${sid}; Path=/; HttpOnly; Secure; SameSite=Strict`);
  res.json({ token });
});

app.post("/transfer", (req, res) => {
  const sid = (req.headers["x-session-id"]);
  const headerToken = req.headers["x-csrf-token"];
  if (!sid || tokens.get(sid) !== headerToken) return res.status(403).json({error:"CSRF"});
  res.json({ ok: true });
});

app.listen(3000);
Enter fullscreen mode Exit fullscreen mode

Harden your cookies:

Set-Cookie: session=...; Path=/; HttpOnly; Secure; SameSite=Strict
Enter fullscreen mode Exit fullscreen mode

React: fetch token once, attach to every request

// src/csrfClient.js
export async function getCsrf() {
  const r = await fetch("/csrf-token", { credentials: "include" });
  const { token } = await r.json();
  // read sid from cookie for demonstration
  const sid = document.cookie.split("; ").find(c=>c.startsWith("sid="))?.split("=")[1];
  return { token, sid };
}
Enter fullscreen mode Exit fullscreen mode
// src/api.js
let CSRF = { token: null, sid: null };

export async function initApi() {
  CSRF = await getCsrf();
}

export async function apiPost(url, body) {
  return fetch(url, {
    method: "POST",
    credentials: "include",
    headers: {
      "Content-Type": "application/json",
      "X-CSRF-Token": CSRF.token,
      "X-Session-Id": CSRF.sid
    },
    body: JSON.stringify(body)
  });
}
Enter fullscreen mode Exit fullscreen mode

📸 Screenshot of the Website Vulnerability Scanner page

Screenshot of the free tools webpage where you can access security assessment tools.Screenshot of the free tools webpage where you can access security assessment tools.


Axios interceptor alternative

// src/axiosClient.js
import axios from "axios";
import { getCsrf } from "./csrfClient";

const api = axios.create({ withCredentials: true });

let csrf = null;
api.interceptors.request.use(async (config) => {
  if (!csrf) csrf = await getCsrf();
  config.headers["X-CSRF-Token"] = csrf.token;
  config.headers["X-Session-Id"] = csrf.sid;
  return config;
});

export default api;
Enter fullscreen mode Exit fullscreen mode

Double-submit cookie check (no server store)

// server/doubleSubmit.js
import crypto from "crypto";
import cookie from "cookie";

export function issueToken(res) {
  const t = crypto.randomBytes(32).toString("hex");
  res.setHeader("Set-Cookie", [
    `csrf=${t}; Path=/; Secure; SameSite=Strict`,
  ]);
  return t;
}

export function validate(req) {
  const cookies = cookie.parse(req.headers.cookie || "");
  const headerT = req.headers["x-csrf-token"];
  return cookies.csrf && headerT && cookies.csrf === headerT;
}
Enter fullscreen mode Exit fullscreen mode

📸 Sample Vulnerability Assessment Report generated by the free tool to check Website Vulnerability

Sample vulnerability assessment report generated with our free tool, providing insights into possible vulnerabilities.Sample vulnerability assessment report generated with our free tool, providing insights into possible vulnerabilities.


Extra hardening checklist

  • Enforce SameSite=Strict (or Lax if you must support normal GET navigations).
  • Require CSRF header on state-changing routes only.
  • Rotate tokens periodically; bind tokens to user/session.
  • Block JSONP/CORS abuse; use strict CORS allowlists.
  • Prefer HttpOnly + server-side verify (or signed JWT tokens).

Keep learning & scanning

Explore more articles on our blog: https://www.pentesttesting.com/blog/
Run a free scan now: https://free.pentesttesting.com/


Managed IT Services (New)

Strengthen uptime, patching, and monitoring with our managed stack.
https://www.pentesttesting.com/managed-it-services/

AI Application Cybersecurity (New)

Secure LLM prompts, model endpoints, and AI data flows end-to-end.
https://www.pentesttesting.com/ai-application-cybersecurity/

Offer Cybersecurity to Your Clients (New)

Agencies/MSPs: white-label our testing & reporting.
https://www.pentesttesting.com/offer-cybersecurity-service-to-your-client/

Newsletter

Subscribe on LinkedIn https://www.linkedin.com/build-relation/newsletter-follow?entityUrn=7327563980778995713

Top comments (0)