Preventing CSRF
CSRF stands for cross-site request forgery.
It is a browser security problem involving authenticated requests.
The basic idea:
- A user is signed in to your site.
- Their browser has cookies for your site.
- Another site causes the browser to send a request to your site.
- 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.
// Good use of GET
fetch("/api/products");Changing data should use methods like POST, PUT, PATCH, or DELETE.
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:
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:
HttpOnlyprevents JavaScript from reading the cookie.Securesends the cookie only over HTTPS.SameSitelimits 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:
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.