Skip to main content

Cross-Site Request Forgery CSRF

Cross-Site Request Forgery, also called CSRF, is an attack where a user is tricked into performing an unwanted action on a website where they are already logged in.

A simple example is a banking website.

User is logged in to bank website

User clicks a malicious link from email/website

That link silently triggers bank transfer API

Bank server receives request with user's valid session

Unwanted transaction may happen

In real banking apps, extra protection like OTP helps prevent this. But if such safeguards are missing, CSRF can become dangerous.

What It Is

CSRF forces an authenticated user to execute unwanted actions on a web application.

Simple meaning:

User is already logged in

Attacker tricks user into clicking/opening something

Browser sends request to trusted website

Cookies/session are automatically included

Server thinks request came from real user

The attacker does not need the user's password. The attacker abuses the user's already-authenticated browser session.

Why It Matters

CSRF is dangerous because the server may trust the request only because the valid session or cookie is present.

A successful CSRF attack can perform actions like:

  • transferring funds
  • changing email address
  • changing password-related settings
  • sending messages
  • updating profile data
  • performing admin actions

If the victim is an admin user, CSRF can compromise the entire web application.

Core Flow

A typical CSRF attack flow looks like this:

Attacker creates malicious link/form/image

Victim is already logged in to target website

Victim opens malicious email or website

Browser sends request to target website

Authentication cookie/token is automatically sent

Server processes the unwanted action

Example mental model:

Attacker cannot access your bank directly,
but attacker can trick your logged-in browser to send a bank request.

Main Reasons CSRF Happens

The PDF explains that CSRF happens because of basic web and HTTP behavior.

ReasonMeaning
Statelessness of HTTPEvery HTTP request is treated as a fresh request
Automatic authenticationCookies/tokens may be automatically carried with the request
Weak action APIsSensitive actions may be triggered too easily
Social engineeringUser may be tricked into clicking malicious links/forms

Statelessness of HTTP

HTTP is stateless.

That means:

Every request is independent.
Server checks the request and authentication data each time.

If the browser sends a valid session cookie with a request, the server may treat that request as authenticated.

User Authentication Problem

Cookies and tokens can be automatically sent by the browser.

Example:

User logged in to bank.com

Browser has bank session cookie

Malicious site triggers request to bank.com

Browser may attach bank cookie

Bank server sees authenticated request

This is why CSRF is possible.

Vulnerability: Using GET for State-Changing Actions

One major mistake is using a GET API call to update data or perform an action.

Bad example:

http://bank.com/fundtransfer?accId=21312&amount=10000

If this URL performs a fund transfer, it is dangerous.

Why?

Because attackers can embed this URL in:

  • anchor tags
  • image tags
  • forms
  • emails
  • external websites

Bad anchor example:

<a href="http://bank.com/fundtransfer?accId=21312&amount=10000"> Offer </a>

Bad image example:

<img
src="http://bank.com/fundtransfer?accId=21312&amount=10000"
width="0"
height="0"
/>

If the image loads automatically, the browser may send the request without the user clearly noticing.

Important rule:

Never use GET for actions that update data.

Vulnerability: Hidden Image Request

A malicious email or webpage can hide an API call inside an image tag.

Example:

<img
src="http://bank.com/fundtransfer?acct=224224&amount=500000"
width="0"
height="0"
border="0"
/>

On page load:

Browser loads image

Image URL calls fund transfer endpoint

Request goes to bank.com

If user is logged in, session may be included

This is dangerous because the user may not even click anything.

A malicious email can show a normal-looking button.

Example:

<a href="http://bank.com/fundtransfer?acct=224224&amount=50000">
Sign Up For Offer
</a>

The user thinks they are clicking an offer, but the link actually triggers a sensitive action.

Flow:

Email says: Click to get offer

User clicks

Redirects to bank transfer URL

Bank action may execute

Vulnerability: Form-Based CSRF

CSRF is not limited to GET requests. A malicious form can also send a POST request.

Example:

<form action="http://bank.com/fundtransfer" method="POST">
<input type="hidden" name="acct" value="224224" />
<input type="hidden" name="amount" value="50000" />
<input type="submit" value="Click to get your free gift!" />
</form>

When the user clicks the button:

Form submits POST request

Hidden account and amount values are sent

Bank endpoint receives request

Unwanted transfer may happen

The user sees a harmless button, but hidden inputs contain the actual sensitive action data.

Main Mitigations

The PDF lists these main CSRF mitigations:

MitigationPurpose
Anti-CSRF tokenServer verifies that the request came from the real form/session
SameSite cookiesControls when cookies are sent in cross-site requests
Referer validationChecks whether request came from expected domain
CAPTCHAAdds human verification
CSP headersAdds browser-level protection
Avoid GET updatesPrevents state change through simple links/images

Anti-CSRF Token

An Anti-CSRF token is a secret token generated by the server and attached to forms or requests.

When the form is submitted, the server checks whether the submitted token matches the token stored on the server.

Flow:

Client opens page

Server generates CSRF token

Server stores token in session

Server sends token inside form

Client submits form with token

Server validates token

Only valid token request is processed

Simple rule:

Every sensitive form/request should include a CSRF token.

How Client Gets CSRF Token

The client gets the CSRF token when it first loads the page or form from the server.

Client sends first request

Server creates CSRF token

Server saves token in session

Server sends token to client form

Example hidden input:

<input type="hidden" name="csrf_token" value="SERVER_GENERATED_TOKEN" />

When form is submitted, this token goes back to the server.

Anti-CSRF Token Example

A simplified Express example:

const express = require("express");
const bodyParser = require("body-parser");
const session = require("express-session");
const crypto = require("crypto");

const app = express();
const port = 3000;

app.use(
session({
secret: "your_secret_key",
resave: false,
saveUninitialized: true,
}),
);

app.use(bodyParser.urlencoded({ extended: false }));

app.get("/", (req, res) => {
if (!req.session.csrfToken) {
req.session.csrfToken = crypto.randomBytes(32).toString("hex");
}

res.send(`
<form action="/fundtransfer" method="POST">
<input type="hidden" name="acct" value="224224" />
<input type="hidden" name="amount" value="50000" />

<input
type="hidden"
name="csrf_token"
value="${req.session.csrfToken}"
/>

<input type="submit" value="Transfer" />
</form>
`);
});

app.post("/fundtransfer", (req, res) => {
const submittedToken = req.body.csrf_token;

if (!submittedToken || submittedToken !== req.session.csrfToken) {
return res.status(403).send("CSRF Token Validation Failed!");
}

delete req.session.csrfToken;

res.send("Form submitted successfully!");
});

app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});

What this does:

Creates token

Stores token in server session

Sends token in form

Checks token on POST request

Rejects request if token is missing or wrong

SameSite Cookies

SameSite controls when cookies are sent with cross-site requests.

Example:

app.use((req, res, next) => {
res.setHeader(
"Set-Cookie",
"sessionId=abc123; SameSite=Strict; Secure; HttpOnly",
);
next();
});

SameSite values:

ValueMeaning
StrictBrowser sends cookie only for same-site requests
LaxCookie is not sent on most cross-site requests
NoneCookie is sent with both cross-site and same-site requests

Simple mental model:

Strict -> strongest same-site restriction
Lax -> balanced restriction
None -> allows cross-site cookies

Modern browsers require SameSite=None cookies to also use Secure.

For CSRF protection, stricter cookie behavior helps reduce the chance that authentication cookies are sent during malicious cross-site requests.

Referer-Based Validation

Referer validation checks where the request came from.

Server reads the Referer header and verifies whether it starts with the expected domain.

Example:

const express = require("express");
const bodyParser = require("body-parser");

const app = express();
const port = 3000;

app.use(bodyParser.urlencoded({ extended: false }));

app.use((req, res, next) => {
const referer = req.get("Referer");

if (referer && referer.startsWith("https://yourwebsite.com")) {
next();
} else {
res.status(403).send("Forbidden");
}
});

app.post("/process", (req, res) => {
res.send("Request processed successfully");
});

app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});

Flow:

Request reaches server

Server checks Referer header

Referer is expected domain -> allow
Referer is missing/wrong -> reject

In production, parse the header and compare the origin exactly. Many systems also validate the Origin header for state-changing requests when it is present.

CAPTCHA

CAPTCHA can help protect sensitive actions because it adds human verification.

Example use cases:

  • fund transfer
  • password reset
  • account setting changes
  • suspicious actions

Mental model:

CSRF tries to automate unwanted action.
CAPTCHA adds a human verification step.

CSP Headers

The PDF also mentions using CSP headers.

CSP headers can reduce the risk of unsafe content execution and help control what resources can load.

For CSRF, CSP is not the only protection, but it adds another browser-level security layer.

Simple rule:

Use CSRF token and SameSite cookies as primary protection.
Use CSP as additional defense.

Why GET Should Not Update Data

GET requests should be used for reading data, not changing data.

Bad:

GET /fundtransfer?acct=224224&amount=50000

Better idea:

POST /fundtransfer

But remember:

POST alone does not prevent CSRF.
POST must also use CSRF protection.

Use:

POST + CSRF token + SameSite cookies + validation

Some Good Practices

The PDF mentions these user and application practices:

  • Always log out of bank apps when done.
  • Create complex passwords.
  • Do not save passwords in the browser.
  • Do not allow the same user to log in from multiple places in sensitive apps like banking apps.
  • Do not use GET method for update operations.

For developers, the most important practice is:

Do not depend only on login session.
Verify that sensitive requests are intentional.

CSRF Testing

The PDF mentions:

putsmail.com/tests/new

This can be used for testing email-based scenarios.

CSRF testing should include:

  • malicious link test
  • hidden image request test
  • form POST test
  • missing CSRF token test
  • wrong CSRF token test
  • SameSite cookie behavior test
  • Referer validation test

CSRF vs XSS

TopicMeaning
CSRFTricks authenticated user/browser into sending unwanted request
XSSInjects malicious script into a page
Main abuseCSRF abuses trust in authenticated requests
Main protectionCSRF tokens, SameSite cookies, Referer checks
ExampleLogged-in user clicks malicious transfer link

Simple comparison:

XSS runs malicious script in the site.
CSRF sends malicious request using user's logged-in session.

Basic Checklist

Use this checklist to prevent CSRF:

  • Do not use GET for update/delete/transfer actions.
  • Use Anti-CSRF tokens for forms and sensitive requests.
  • Generate CSRF tokens on the server.
  • Store CSRF tokens in the server session.
  • Validate CSRF token before processing sensitive actions.
  • Regenerate or clear CSRF token after use when needed.
  • Use SameSite cookies.
  • Prefer stricter cookie behavior for sensitive apps.
  • Validate the Referer header where appropriate.
  • Use CAPTCHA for highly sensitive operations.
  • Add CSP headers as defense-in-depth.
  • Do not rely only on authentication cookies.
  • Avoid hidden or automatic state-changing actions.
  • Test CSRF flows using malicious links, images, and forms.

Interview Style Answer

Cross-Site Request Forgery, or CSRF, is an attack where an authenticated user is tricked into performing an unwanted action on a trusted website. For example, if a user is logged in to a bank website and clicks a malicious link or opens a malicious email, the browser may automatically send the user's session cookie with a fund transfer request.

CSRF happens because HTTP is stateless and authentication cookies or tokens may be automatically carried with requests. Common vulnerabilities include using GET requests for state-changing actions, hidden image requests, malicious links, and form-based POST attacks.

To prevent CSRF, we should use Anti-CSRF tokens, validate tokens on the server, use SameSite cookies, validate the Referer header, use CAPTCHA for sensitive operations, add CSP headers, and never use GET for update operations.

One-Line Summary

CSRF tricks a logged-in user's browser into sending an unwanted authenticated request to a trusted website.

Final Mental Model

Logged-in user + malicious link/form + auto-sent cookie = CSRF risk

Remember it like this:

Authentication proves who the user is.
CSRF token proves the action came from the real form.
SameSite controls when cookies travel.
Referer checks where the request came from.
GET should never change data.

Or:

Do not process sensitive actions just because a valid cookie came with the request.