text 12 min

Preventing CSRF

CSRF stands for cross-site request forgery.

It is a browser security problem involving authenticated requests.

The basic idea:

  1. A user is signed in to your site.
  2. Their browser has cookies for your site.
  3. Another site causes the browser to send a request to your site.
  4. If your server accepts that request, an action may happen without the user intending it.

Modern browsers and frameworks provide protections, but you should understand the concept.

Why Cookies Matter

Browsers automatically include matching cookies with requests.

If a user is logged in with a session cookie, the browser may send that cookie whenever it makes a request to the same site.

That automatic behavior is convenient.

It also means your server must be careful about state-changing requests.

State-changing requests include:

  • creating an order
  • updating an email address
  • deleting a resource
  • changing a password
  • transferring money

Safe and Unsafe Request Methods

GET requests should read data.

They should not change important server state.

js
// Good use of GET
fetch("/api/products");

Changing data should use methods like POST, PUT, PATCH, or DELETE.

js
await fetch("/api/profile", {
  method: "PATCH",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ displayName: "Asha" }),
});

This does not solve CSRF by itself, but it makes your API easier to protect.

CSRF Tokens

A CSRF token is a secret value generated by the server and included with legitimate requests.

The server checks that the token is present and correct before accepting a state-changing request.

Example shape:

js
const csrfToken = document
  .querySelector('meta[name="csrf-token"]')
  .getAttribute("content");

await fetch("/api/settings", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-CSRF-Token": csrfToken,
  },
  body: JSON.stringify({ theme: "dark" }),
});

The exact token handling usually comes from your backend framework.

As a frontend developer, your job is often to send the token correctly.

SameSite Cookies

Cookies can include a SameSite attribute.

It tells the browser when cookies should be sent with cross-site requests.

Common values:

Value Meaning
Strict send the cookie only for same-site navigation and requests
Lax send the cookie for most same-site requests and top-level safe navigation
None allow cross-site sending, but requires Secure

For many session cookies, SameSite=Lax or SameSite=Strict reduces CSRF risk.

Secure Cookie Flags

Sensitive cookies should usually be configured by the server with strong flags.

Important flags:

  • HttpOnly prevents JavaScript from reading the cookie.
  • Secure sends the cookie only over HTTPS.
  • SameSite limits cross-site sending.

Frontend JavaScript cannot set HttpOnly.

That flag must be set by the server.

CORS Is Not CSRF Protection

CORS controls whether browser JavaScript can read cross-origin responses.

It is not the same as authentication.

It is also not a complete CSRF defense.

A cross-site request may still be sent even if the response cannot be read by the attacking page.

Your server should still protect state-changing routes.

Credentials with Fetch

By default, fetch() does not always send cookies for cross-origin requests.

You may see code like this when a frontend and API are on different origins:

js
await fetch("https://api.example.com/account", {
  method: "POST",
  credentials: "include",
  headers: {
    "Content-Type": "application/json",
    "X-CSRF-Token": csrfToken,
  },
  body: JSON.stringify({ email: "asha@example.com" }),
});

Only use credentials: "include" when your server is configured for it and you understand which cookies will be sent.

Common Mistakes

Do not use GET routes for destructive actions.

Do not assume CORS replaces CSRF protection.

Do not store CSRF tokens in places where unrelated scripts can easily leak them.

Do not accept state-changing requests just because the user has a valid session cookie.

Do not turn off framework CSRF protection to "fix" a request without understanding why it failed.

Summary

CSRF is about unwanted authenticated requests.

Protect state-changing routes with server-side checks, CSRF tokens when appropriate, and careful cookie settings.

Use safe HTTP methods correctly, and remember that CORS and CSRF solve different problems.