Security
Cross-Site Request Forgery (CSRF)
Master Cross-Site Request Forgery (CSRF) in JavaScript. Learn how attackers hijack session state cookies and implement CSRF token guards.
1. Introduction
Cross-Site Request Forgery (CSRF) is a security vulnerability where an attacker forces a user's browser to perform unauthorized actions on a trusted website where the user is currently authenticated.
2. Why It Matters
Browsers automatically include authentication cookies with requests made to a target site, regardless of where the request originates. If a user is logged into their bank and visits a malicious site, the malicious site can submit a transfer request to the bank, and the browser will attach the user's session cookie, executing the transfer.
3. Real-World Analogy
Think of a Forged Signature Authorizing a Transaction:
- Standard Request (Signing bank check): You fill out a bank check, sign it, and hand it to the teller to transfer funds.
- CSRF Attack (Forged delivery envelope): An attacker writes a transfer request on a sheet of paper. They place it in a pre-paid courier envelope that has your name and return address on it (the automatic browser cookie). The courier delivers the envelope. Because the teller sees your address and verification signature, they process the transfer, even though you didn't send the request.
4. CSRF Vulnerability Example
If a bank site performs transfers using a simple GET endpoint, an attacker can trigger the transfer by embedding an image tag on a malicious site:
When the user visits the attacker's site, the browser attempts to load the image, sending a request to the bank. Since the user is logged into the bank, the browser attaches the bank's authentication cookie, authorizing the transfer automatically.
5. Prevention Strategies
To protect your application from CSRF:
• Anti-CSRF Tokens (Synchronizer Token Pattern): The server generates a unique, cryptographically secure token associated with the user's session. The client must include this token in POST/PUT request bodies or headers. The server validates the token before executing the action.
• SameSite Cookie Attribute: Configure session cookies with the SameSite attribute set to Lax or Strict. This tells the browser not to send cookies with cross-site requests, blocking CSRF attacks completely.
• Custom Request Headers: Require custom request headers (like X-Requested-With) for API calls. Since cross-origin requests cannot set custom headers without CORS approval, this blocks unauthorized actions.
6. Practical Example
This script demonstrates reading an anti-CSRF token from a meta tag and appending it to all outgoing fetch request headers:
7. Common Mistakes
- Relying on GET requests for state-changing operations: GET requests are designed to be safe and idempotent (read-only). Browsers can pre-fetch them, and attackers can trigger them easily using standard image or link tags. Never use GET endpoints for actions that modify state.
8. Quick Quiz
Q1: Which cookie attribute prevents the browser from sending cookies during cross-site requests?
A) HttpOnly
B) SameSite (Lax or Strict)
Answer: B — SameSite blocks the browser from attaching cookies to cross-origin requests, preventing CSRF attacks.
9. Scenario-Based Challenge
The CSRF-Safe Account Deletion Form:
An admin portal deletes user accounts:
10. Debugging Exercise
Explain why this token-based verification system is still vulnerable to CSRF, and how to fix it:
// Express backend route app.post('/transfer', (req, res) => { const clientToken = req.body.csrfToken;
// Bug: comparing clientToken to a static token stored in a global config! if (clientToken !== globalConfig.staticToken) { return res.status(403).send('Invalid Token'); } // execution... });
View Solution
Diagnosis: The backend validates the CSRF token against a static, global token. Once an attacker obtains the token (e.g. via XSS or reading source files), the token remains valid for all users, rendering the protection useless.
Fix: Generate unique, session-scoped CSRF tokens that are verified against the user's specific session store:
app.post('/transfer', (req, res) => {
// Verify token against the unique session store
if (req.body.csrfToken !== req.session.csrfToken) {
return res.status(403).send('Invalid Token');
}
// execution...
});
11. Interview Questions
🟢 Q1: Explain how the SameSite cookie attribute works to prevent CSRF attacks.
Answer: The SameSite attribute controls whether cookies are sent with cross-site requests:
• SameSite=Strict: The cookie is never sent in cross-site requests (e.g. if the user clicks a link to your site from an external site, the cookie is not sent).
• SameSite=Lax: The cookie is sent during cross-site navigations that use safe HTTP methods (like clicking a GET link to your site), but is blocked for state-changing requests (like POST forms or image embeds).
Setting SameSite=Lax or Strict prevents the browser from attaching session cookies to unauthorized cross-origin requests, blocking CSRF attacks.
12. Production Considerations
- • JWT and LocalStorage: Storing JWT tokens in local storage instead of cookies makes your application immune to CSRF because local storage tokens must be attached manually by client scripts. However, this makes them highly vulnerable to XSS attacks. If using cookies, always enable the
HttpOnlyandSameSiteflags.