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.
| Reason | Meaning |
|---|---|
| Statelessness of HTTP | Every HTTP request is treated as a fresh request |
| Automatic authentication | Cookies/tokens may be automatically carried with the request |
| Weak action APIs | Sensitive actions may be triggered too easily |
| Social engineering | User 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.
Vulnerability: Malicious Link in Email
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:
| Mitigation | Purpose |
|---|---|
| Anti-CSRF token | Server verifies that the request came from the real form/session |
| SameSite cookies | Controls when cookies are sent in cross-site requests |
| Referer validation | Checks whether request came from expected domain |
| CAPTCHA | Adds human verification |
| CSP headers | Adds browser-level protection |
| Avoid GET updates | Prevents 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:
| Value | Meaning |
|---|---|
Strict | Browser sends cookie only for same-site requests |
Lax | Cookie is not sent on most cross-site requests |
None | Cookie 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
GETmethod 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
| Topic | Meaning |
|---|---|
| CSRF | Tricks authenticated user/browser into sending unwanted request |
| XSS | Injects malicious script into a page |
| Main abuse | CSRF abuses trust in authenticated requests |
| Main protection | CSRF tokens, SameSite cookies, Referer checks |
| Example | Logged-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
GETfor 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
SameSitecookies. - Prefer stricter cookie behavior for sensitive apps.
- Validate the
Refererheader 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.