Cross-Site Request Forgery and Cross-Site Scripting are responsible for a huge proportion of web security incidents. Day 3 covers what they are and exactly how to prevent them.
CSRF tricks an authenticated user's browser into making an unwanted request to your app. If your API uses cookies for auth, an attacker can embed a request in an image or link that runs with the user's session.
<!-- Attacker's page sends a POST as the logged-in user -->
<img src="https://bank.com/transfer?to=attacker&amount=1000">
<!-- Or a form that auto-submits -->
<form method="POST" action="https://bank.com/transfer">
<input name="to" value="attacker">
<input name="amount" value="1000">
</form>
<script>document.forms[0].submit();</script>npm install csurf
// Note: for modern apps, use SameSite cookies instead
// Method 1: SameSite cookie attribute (preferred)
res.cookie('sessionId', token, {
httpOnly: true,
secure: true, // HTTPS only
sameSite: 'strict', // Never sent cross-site
maxAge: 15 * 60 * 1000 // 15 minutes
});
// Method 2: CSRF token in forms (for traditional web apps)
const csrf = require('csurf');
app.use(csrf({ cookie: true }));
app.get('/form', (req, res) => {
res.render('form', { csrfToken: req.csrfToken() });
});
// In your HTML form:
// <input type="hidden" name="_csrf" value="<%= csrfToken %>">XSS injects malicious scripts into pages viewed by other users. If you render user input without escaping it, attackers can steal sessions, redirect users, or deface your app.
npm install dompurify helmet
// 1. Never render user input as raw HTML
// WRONG:
element.innerHTML = userInput;
// RIGHT:
element.textContent = userInput; // Auto-escapes
// 2. If you MUST render HTML, sanitize it first
import DOMPurify from 'dompurify';
element.innerHTML = DOMPurify.sanitize(userInput);
// 3. Content Security Policy headers (server-side)
const helmet = require('helmet');
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"], // No inline scripts
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
}
}
}));