The Basics You Cannot Skip
Security Checklist
The minimum-viable security checklist for a Node web app. Each item prevents a common attack class.
What you'll learn
- Apply Helmet, CORS, rate-limit
- Avoid the OWASP Top 10 in Node
- Keep deps updated
The biggest security wins are the cheapest. Below is a ship-it-tomorrow checklist for a Node web app.
✅ Use Helmet
npm install helmet import helmet from "helmet";
app.use(helmet()); One line sets ~12 security headers: CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, etc.
✅ Validate All Input
Use Zod (covered earlier) on every request body, every query string. Untrusted input is the root of most vulnerabilities.
✅ Parameterized SQL — Never Concat
// NEVER
db.query(`SELECT * FROM users WHERE email = '${email}'`);
// ALWAYS
db.query("SELECT * FROM users WHERE email = $1", [email]); Same for shell commands — use execFile([...args]), never
exec with concatenation.
✅ Hash Passwords with bcrypt/argon2
No plaintext. No sha256. Use a slow KDF.
✅ Set Secure Cookie Attributes
HttpOnly + Secure + SameSite=Lax on session cookies. Always.
✅ Rate-Limit Login Endpoints
5 attempts per 15 minutes per IP. Prevents brute force.
✅ CORS Allow-List
Never Access-Control-Allow-Origin: * with credentials. List the
origins you actually trust.
✅ Don’t Expose Stack Traces
In production, the error response should be generic. Stack traces land in your logs, not on the client.
✅ Keep Deps Updated
npm audit
npm audit fix Better: use Dependabot or Renovate to open PRs as updates land. Fewer surprises.
✅ Lock Down Env Vars
Never log them. Never commit .env. Rotate compromised secrets
immediately.
✅ HTTPS Everywhere
In production, HTTPS only. Set HSTS. The reverse proxy or CDN handles this.
✅ Watch for SSRF
If your server fetches URLs the user provides, validate that the
URL doesn’t point to localhost, 169.254.169.254 (AWS metadata),
or internal IPs.
import { URL } from "node:url";
function isSafeURL(input) {
const u = new URL(input);
if (!["http:", "https:"].includes(u.protocol)) return false;
if (["localhost", "127.0.0.1", "0.0.0.0", "169.254.169.254"].includes(u.hostname)) return false;
return true;
} The OWASP Top 10
If you have time for one external resource: read the OWASP Top 10. Each entry is a vulnerability class. Make sure none apply to your app.
Going Further →