Building Your Learning Module...
Getting things ready for you!
Find videos you like?
Save to resource drawer for future reference!
CSRF is an attack that tricks a user's browser into making unwanted requests to a website where they're authenticated. Attackers exploit the browser's automatic inclusion of cookies to perform actions on behalf of the victim.
User is logged into bank.com. Attacker sends email with hidden form:
❌ Browser sends cookies automatically → Money transferred without user's knowledge!
<img src="bank.com/transfer?to=attacker">// ❌ VULNERABLE: No CSRF token verification
app.post('/transfer', (req, res) => {
// Only checks if user is authenticated via cookie
if (!req.session.userId) {
return res.status(401).send('Unauthorized');
}
// Performs action without verifying request origin
const { to, amount } = req.body;
transferMoney(req.session.userId, to, amount);
res.send('Transfer successful');
});
// ❌ VULNERABLE: Frontend form without token
<form action="/transfer" method="POST">
<input name="to" value="recipient-account">
<input name="amount" value="100">
<button>Transfer</button>
</form>
// Attacker can trigger this from ANY website!Generate unique token per session/request
Browser prevents cookies in cross-site requests
Send token in both cookie and request body
Leverage CORS to require custom headers
// Backend: Generate and validate CSRF tokens
import crypto from 'crypto';
// Generate CSRF token
function generateCSRFToken() {
return crypto.randomBytes(32).toString('hex');
}
// Middleware to generate token
app.use((req, res, next) => {
if (!req.session.csrfToken) {
req.session.csrfToken = generateCSRFToken();
}
res.locals.csrfToken = req.session.csrfToken;
next();
});
// Middleware to verify token
function verifyCSRFToken(req, res, next) {
const token = req.body._csrf || req.headers['x-csrf-token'];
if (!token || token !== req.session.csrfToken) {
return res.status(403).json({ error: 'Invalid CSRF token' });
}
next();
}
// ✅ PROTECTED: Transfer endpoint with CSRF protection
app.post('/transfer', verifyCSRFToken, (req, res) => {
if (!req.session.userId) {
return res.status(401).send('Unauthorized');
}
const { to, amount } = req.body;
transferMoney(req.session.userId, to, amount);
res.send('Transfer successful');
});
// Render token in page
app.get('/transfer-page', (req, res) => {
res.render('transfer', { csrfToken: req.session.csrfToken });
});// ✅ Method 1: Hidden field in form
<form action="/transfer" method="POST">
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
<input name="to" value="recipient-account">
<input name="amount" value="100">
<button>Transfer</button>
</form>
// ✅ Method 2: AJAX with custom header
function transferMoney(to, amount) {
// Get token from meta tag
const token = document.querySelector('meta[name="csrf-token"]').content;
fetch('/transfer', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': token // Custom header
},
credentials: 'same-origin', // Include cookies
body: JSON.stringify({ to, amount })
})
.then(response => {
if (!response.ok) throw new Error('Transfer failed');
return response.json();
})
.then(data => console.log('Success:', data))
.catch(error => console.error('Error:', error));
}
// ✅ Method 3: Axios with interceptor
import axios from 'axios';
// Set CSRF token for all requests
axios.defaults.xsrfCookieName = 'csrftoken';
axios.defaults.xsrfHeaderName = 'X-CSRF-Token';
// Or use interceptor
axios.interceptors.request.use(config => {
const token = document.querySelector('meta[name="csrf-token"]').content;
config.headers['X-CSRF-Token'] = token;
return config;
});
// Now all requests include token automatically
axios.post('/transfer', { to: 'account', amount: 100 });// ✅ Set SameSite attribute on session cookies
// Express with cookie-session
app.use(cookieSession({
name: 'session',
secret: 'your-secret-key',
sameSite: 'strict', // or 'lax'
httpOnly: true, // Prevent JavaScript access
secure: true, // HTTPS only
maxAge: 24 * 60 * 60 * 1000 // 24 hours
}));
// Express with express-session
app.use(session({
secret: 'your-secret-key',
cookie: {
sameSite: 'strict',
httpOnly: true,
secure: true,
maxAge: 24 * 60 * 60 * 1000
}
}));
// Manual cookie setting
res.cookie('sessionId', sessionId, {
sameSite: 'strict', // Prevents cross-site usage
httpOnly: true,
secure: true
});
/*
SameSite Values:
- Strict: Never send cookie cross-site (most secure)
- Lax: Send on top-level navigation (GET only)
- None: Always send (requires Secure flag)
*/
// ✅ Example with different SameSite values
// Strict - No cross-site cookies at all
res.cookie('auth', token, { sameSite: 'strict' });
// Even clicking link from google.com won't send cookie
// Lax - Allows safe cross-site navigation
res.cookie('auth', token, { sameSite: 'lax' });
// Link from google.com → yoursite.com (cookie sent)
// Form POST from evil.com → yoursite.com (cookie NOT sent)
// None - No protection (not recommended)
res.cookie('tracking', id, { sameSite: 'none', secure: true });
// Only use for legitimate cross-site scenariosGenerate unique tokens per session
Verify on every state-changing request
Set SameSite=Lax or Strict
Modern, effective CSRF defense
Use POST/PUT/DELETE only
GET should be read-only
Tokens + SameSite + Origin checks
Defense in depth approach