Skip to main content
Practice Problems

What is CSRF and how to prevent it?

CSRF (Cross-Site Request Forgery) is a web security vulnerability that allows attackers to trick users into performing unwanted actions on a website where they're authenticated.

How CSRF Works

Attack Scenario

  1. User logs into bank.com (gets session cookie)
  2. User visits malicious site evil.com (while still logged in to bank.com)
  3. evil.com sends forged request to bank.com
  4. Browser automatically includes bank.com cookies
  5. Bank processes request as if user initiated it

Example Attack

Vulnerable Bank Transfer:

html
<!-- On evil.com --> <img src="https://bank.com/transfer?to=attacker&amount=1000" /> <!-- or --> <form action="https://bank.com/transfer" method="POST"> <input type="hidden" name="to" value="attacker" /> <input type="hidden" name="amount" value="1000" /> </form> <script> document.forms[0].submit(); </script>

When user loads this page, the request is automatically sent with their bank.com cookies!

Prevention Methods

1. CSRF Tokens (Most Common)

Generate unique token for each session/request:

Backend (Node.js/Express):

javascript
const csrf = require('csurf'); const csrfProtection = csrf({ cookie: true }); app.get('/form', csrfProtection, (req, res) => { // Generate CSRF token res.render('form', { csrfToken: req.csrfToken() }); }); app.post('/transfer', csrfProtection, (req, res) => { // Token automatically validated if (valid) { processTransfer(req.body); } });

Frontend:

html
<form action="/transfer" method="POST"> <input type="hidden" name="_csrf" value="{{ csrfToken }}" /> <input name="to" /> <input name="amount" /> <button>Transfer</button> </form>

With React/Fetch:

javascript
function Transfer() { const [csrfToken, setCsrfToken] = useState(''); useEffect(() => { fetch('/api/csrf-token') .then(r => r.json()) .then(data => setCsrfToken(data.token)); }, []); const handleTransfer = async () => { await fetch('/api/transfer', { method: 'POST', headers: { 'Content-Type': 'application/json', 'CSRF-Token': csrfToken // Custom header }, body: JSON.stringify({ to, amount }) }); }; return <button onClick={handleTransfer}>Transfer</button>; }

2. SameSite Cookies

Prevent browser from sending cookies with cross-site requests:

javascript
// Node.js/Express res.cookie('sessionId', sessionId, { httpOnly: true, secure: true, sameSite: 'strict' // or 'lax' });

SameSite Values:

  • strict: Cookie never sent on cross-site requests
  • lax: Cookie sent on top-level navigations (GET only)
  • none: Cookie sent on all requests (requires secure)

3. Custom Request Headers

Require custom headers that can't be set by forms:

javascript
// Frontend fetch('/api/transfer', { method: 'POST', headers: { 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); // Backend app.post('/api/transfer', (req, res) => { if (req.headers['x-requested-with'] !== 'XMLHttpRequest') { return res.status(403).send('Forbidden'); } // Process transfer });

4. Verify Origin/Referer Headers

javascript
app.post('/api/transfer', (req, res) => { const origin = req.headers.origin || req.headers.referer; if (!origin || !origin.startsWith('https://bank.com')) { return res.status(403).send('Invalid origin'); } // Process transfer });

5. Re-authentication for Sensitive Actions

javascript
app.post('/api/delete-account', async (req, res) => { // Require password confirmation const user = await User.findById(req.userId); if (!await user.checkPassword(req.body.password)) { return res.status(401).send('Invalid password'); } // Delete account });

Real-World Example: Complete Protection

javascript
// Express.js with multiple protections const express = require('express'); const csrf = require('csurf'); const cookieParser = require('cookie-parser'); const app = express(); // 1. SameSite cookies app.use(cookieParser()); app.use((req, res, next) => { res.cookie('session', req.sessionID, { httpOnly: true, secure: true, sameSite: 'strict' }); next(); }); // 2. CSRF tokens const csrfProtection = csrf({ cookie: true }); // 3. Origin verification const verifyOrigin = (req, res, next) => { const origin = req.headers.origin; const allowedOrigins = ['https://myapp.com']; if (origin && !allowedOrigins.includes(origin)) { return res.status(403).send('Forbidden'); } next(); }; // Protected route with multiple defenses app.post('/api/transfer', verifyOrigin, csrfProtection, (req, res) => { // Process transfer } );

Testing for CSRF Vulnerabilities

html
<!-- Test page to check if site is vulnerable --> <!DOCTYPE html> <html> <body> <h1>CSRF Test</h1> <form id="testForm" action="https://target-site.com/api/action" method="POST"> <input type="hidden" name="data" value="malicious" /> </form> <script> // Auto-submit when page loads document.getElementById('testForm').submit(); </script> </body> </html>

If action executes without additional verification, site is vulnerable!

Common Interview Questions

  1. Q: What's the difference between CSRF and XSS? A: CSRF exploits user's authentication to perform actions. XSS injects malicious scripts. CSRF uses user as "confused deputy", XSS directly attacks user.

  2. Q: Why can't attackers read CSRF token from the page? A: Due to Same-Origin Policy, JavaScript from evil.com cannot read content from bank.com.

  3. Q: Is HTTPS enough to prevent CSRF? A: No! HTTPS prevents MITM attacks but doesn't stop CSRF. You still need CSRF tokens or other protections.

  4. Q: Can GET requests have CSRF vulnerabilities? A: Yes! If GET requests change state (delete, transfer, etc.), they're vulnerable. Always use POST/PUT/DELETE for state changes.

Best Practices

DO:

  • Use CSRF tokens for state-changing operations
  • Set SameSite=Strict for session cookies
  • Verify Origin/Referer headers
  • Use POST/PUT/DELETE for state changes (never GET)
  • Require re-authentication for sensitive actions

DON'T:

  • Rely on HTTPS alone
  • Use GET for state-changing operations
  • Trust cookies alone for authentication
  • Forget to validate tokens on every request
  • Store CSRF tokens in cookies (use hidden fields or custom headers)

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems