Cross-Site Request Forgery (CSRF) is a web attack where an authenticated user's browser is tricked into sending unauthorized requests to a trusted application — without the user's knowledge. Unlike XSS, which injects malicious scripts into a page, CSRF exploits the trust a server places in a user's browser session. A single successful CSRF attack can trigger fund transfers, change account settings, or escalate privileges — all using the victim's own authenticated session. This guide explains how CSRF works, how it differs from XSS, and the prevention controls that every Indian developer should implement today.
What Is CSRF and How Does It Differ from XSS?
CSRF and Cross-Site Scripting (XSS) are both browser-level attacks, but they exploit different trust relationships.
XSS attacks the user's trust in the application — malicious script is injected into a page and runs in the victim's browser, stealing cookies, tokens, or session data.
CSRF attacks the application's trust in the user's browser — the attacker crafts a forged request that the victim's authenticated browser sends on the attacker's behalf. The application sees a valid session cookie and honors the request.
| Attribute | XSS | CSRF |
|---|---|---|
| Exploit target | User's trust in the site | Server's trust in the browser |
| Attack vector | Injected script in the page | Forged HTTP request from another origin |
| Session required on victim | No | Yes — victim must be logged in |
| Visible to victim | Sometimes | Almost never |
| Impact | Data theft, session hijack | Unauthorized state-changing actions |
| Primary defense | Content Security Policy, output encoding | Anti-CSRF tokens, SameSite cookies |
How a CSRF Attack Works: Step by Step
graph TD
A[Victim logs in to bank.example.in] --> B[Server issues session cookie]
B --> C[Victim visits attacker controlled page]
C --> D[Malicious page auto-submits hidden form]
D --> E[Browser attaches session cookie automatically]
E --> F[Bank server receives forged transfer request]
F --> G{CSRF token present?}
G -->|No token - VULNERABLE| H[Transfer executes as victim]
G -->|Token validated - SAFE| I[Request rejected 403]
style A fill:#1e3a5f,stroke:#3B82F6,color:#e2e8f0
style B fill:#1e3a5f,stroke:#3B82F6,color:#e2e8f0
style C fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0
style D fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0
style E fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0
style F fill:#1e3a5f,stroke:#3B82F6,color:#e2e8f0
style G fill:#1e3a5f,stroke:#3B82F6,color:#e2e8f0
style H fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0
style I fill:#1e3d2f,stroke:#10B981,color:#e2e8f0The attack works because browsers automatically send cookies for a domain with every request to that domain — regardless of which page originated the request. The server cannot distinguish a genuine user action from a forged one unless additional verification is in place.
A classic attack scenario in the Indian fintech context:
- A user logs into their net-banking portal. The server sets a session cookie.
- Without logging out, the user opens a phishing email and clicks a link to
attacker.example.com. - That page contains a hidden HTML form pointing to
bank.example.in/transferwith pre-filled amount and beneficiary. - JavaScript auto-submits the form. The browser dutifully appends the session cookie.
- The bank server receives a fully authenticated-looking request and processes the transfer.
Real-World Impact
CSRF is not theoretical. The OWASP Top 10 has listed it as a critical vulnerability category across multiple editions, and it appears consistently in penetration test findings for Indian banking, e-commerce, and government portals.
Common high-impact CSRF scenarios:
- Fund transfers — Trigger a payment or NEFT/IMPS transfer to an attacker-controlled account.
- Email or mobile number change — Lock the user out of account recovery.
- Password reset initiation — Chain with CSRF to take over an account.
- Admin privilege escalation — On internal tools, force an admin to grant attacker roles.
- Data deletion — DELETE endpoints without CSRF protection allow mass data wipes.
- OAuth token grant — Force a user to authorize a malicious third-party application.
Know your vulnerabilities before attackers do
Run a free VAPT scan — takes 5 minutes, no signup required.
Book Your Free ScanVulnerable vs. Fixed Code Examples
Vulnerable: No CSRF Protection (Node.js / Express)
// VULNERABLE — no CSRF token check
app.post('/api/transfer', requireAuth, async (req, res) => {
const { toAccount, amount } = req.body;
await transferFunds(req.user.id, toAccount, amount);
res.json({ success: true });
});Any authenticated user's browser can be made to hit this endpoint from a third-party page.
Fixed: CSRF Token Validation (Node.js / Express + csurf pattern)
// Note: the 'csurf' npm package was deprecated in 2023.
// Use csrf-csrf, lusca, or your framework's built-in CSRF middleware instead.
// The synchronizer-token pattern below is framework-agnostic.
// Generate and bind token to session
app.get('/transfer-form', (req, res) => {
const csrfToken = req.session.csrfToken || crypto.randomBytes(32).toString('hex');
req.session.csrfToken = csrfToken;
res.render('transfer', { csrfToken });
});
// Validate token on state-changing request
app.post('/api/transfer', requireAuth, async (req, res) => {
const tokenFromRequest = req.body._csrf || req.headers['x-csrf-token'];
if (!req.session.csrfToken || !timingSafeEqual(req.session.csrfToken, tokenFromRequest)) {
return res.status(403).json({ error: 'Invalid CSRF token' });
}
const { toAccount, amount } = req.body;
await transferFunds(req.user.id, toAccount, amount);
res.json({ success: true });
});The CSRF token is unique per session and per form render. An attacker on a different origin cannot read it (same-origin policy blocks cross-origin reads), so they cannot forge a valid request.
Fixed: SameSite Cookie Flag (works alongside tokens)
// Set session cookie with SameSite=Strict or Lax
res.cookie('session', token, {
httpOnly: true,
secure: true,
sameSite: 'Strict', // or 'Lax' for broader compatibility
});With SameSite=Strict, the browser will not send the cookie on any cross-site request — the forged form submission gets no session attached. Lax is the minimum acceptable setting for new projects; None (with Secure) re-enables cross-site sending and must only be used when genuinely required (e.g., third-party embedded widgets).
Prevention Controls: A Comprehensive Checklist
pie title CSRF Prevention Coverage by Control Type
"Anti-CSRF Tokens" : 38
"SameSite Cookies" : 27
"Origin/Referer Check" : 16
"Double-Submit Cookie" : 12
"Re-auth for Sensitive Actions" : 7Distribution reflects relative adoption weight across OWASP-recommended controls, not empirical survey data.
1. Anti-CSRF Tokens (Synchronizer Token Pattern)
The gold standard. Generate a cryptographically random token, bind it to the user session, embed it in every state-changing form or AJAX request header, and validate it server-side before processing. Tokens must be:
- Unpredictable (use
crypto.randomBytes(32)or equivalent) - Per-session at minimum; per-request for high-value operations
- Validated with a constant-time comparison to prevent timing attacks
2. SameSite Cookie Attribute
Set SameSite=Strict or SameSite=Lax on all session cookies. This is a browser-enforced defense that requires no application logic changes. Chrome has defaulted to Lax when the attribute is omitted since version 80 (2020), and most modern browsers follow suit — but explicit setting is still required for compliance, older browser compatibility, and to ensure predictable behaviour across all contexts.
3. Checking Origin and Referer Headers
For server-rendered applications and APIs, validate that the Origin or Referer header matches your expected domain before processing state-changing requests. This is a defense-in-depth measure, not a primary control — headers can be absent (privacy tools strip them), so it should complement, not replace, token validation.
function validateOrigin(req) {
const origin = req.headers.origin || req.headers.referer;
if (!origin) return false; // fail closed
const allowed = ['https://www.yourapp.in', 'https://app.yourapp.in'];
return allowed.some(o => origin.startsWith(o));
}4. Double-Submit Cookie Pattern
For stateless APIs where server-side session storage is not available: set a random value in both a cookie and a request header. The server verifies they match. Because an attacker cannot read the cookie from a different origin (same-origin policy), they cannot replicate it in the header.
5. Re-authentication for Sensitive Actions
For high-value operations — password change, bank transfer above a threshold, adding a new beneficiary — require the user to re-enter their password or complete OTP verification immediately before the action. Even if a CSRF attack bypasses token checks (through a misconfiguration), it cannot supply the user's password.
6. Avoid GET for State-Changing Operations
GET requests should never modify state. Browsers, crawlers, and link prefetching all follow GET links without user intent. If a GET endpoint deletes a record or triggers a transfer, it is trivially exploitable without any cross-site interaction at all — just a crafted <img src="..."> tag suffices.
CSRF in Modern SPAs and APIs
Single-page applications using JWT tokens stored in localStorage are inherently CSRF-resistant — JWTs are not sent automatically by the browser, unlike cookies. However, if your SPA uses cookie-based sessions (common for security reasons — HttpOnly cookies are inaccessible to JavaScript, unlike localStorage), CSRF protection is still mandatory.
For API-first architectures:
- Use the
X-Requested-With: XMLHttpRequestheader as a lightweight signal (not a substitute for token validation) - Validate
Content-Type: application/json— HTML forms cannot send JSON, so a strict content-type check filters most naive CSRF attempts - For fetch-based APIs, CORS policy controls which origins can make credentialed requests — but CORS is not a CSRF defense for same-origin API consumers
Indian Developer Context: Where CSRF Hurts Most
CSRF vulnerabilities surface most frequently in:
- Fintech and BFSI portals — Transfer endpoints, beneficiary management, and KYC update flows are prime targets. RBI's cybersecurity guidelines for banks explicitly require input validation and session management controls that encompass CSRF.
- E-governance portals — File submission, status update, and payment confirmation endpoints on government-facing applications.
- B2B SaaS admin panels — Internal tools that assume "trusted network" without enforcing CSRF tokens are exploited through phishing attacks on employees.
- Healthcare and HR systems — Patient data updates, salary processing, leave approval workflows — all high-value state-changing operations.
Running a CSRF Audit
Before fixing, you need to find all vulnerable endpoints. A systematic audit covers:
- Map every POST/PUT/PATCH/DELETE endpoint in the application
- For each endpoint: is it authenticated via cookie? If yes, is a CSRF token required?
- Test with a cross-origin form submission tool or Burp Suite's CSRF PoC generator
- Review framework configuration for accidental token exclusions (whitelisted paths, API route groups)
- Verify SameSite attributes on all session-related cookies
For compliance-grade evidence — especially if you are preparing for SEBI CSCRF, RBI IT Framework, or ISO 27001 audits — a full VAPT report documents CSRF findings with proof-of-concept, business impact, and remediation guidance in a format accepted by auditors.