24 Security Best Practices
title: Security Best Practices for Node.js REST APIs weight: 34 date: 2026-06-28 lastmod: 2026-06-28 description: Learn security best practices for Node.js REST APIs including helmet headers, input sanitization, SQL injection prevention, rate limiting, CORS, and dependency auditing. tags: [api-development, nodejs]
Security best practices for Node.js REST APIs include HTTP security headers with Helmet, input validation and sanitization, SQL/NoSQL injection prevention, proper authentication, rate limiting, CORS configuration, and regular dependency auditing.
```mermaid
flowchart TD
A[Security Layers] --> B[Helmet Headers]
A --> C[Input Validation]
A --> D[Auth + Rate Limiting]
A --> E[Dependency Audit]
B --> F[CSP, X-Frame-Options]
C --> G[Sanitize, Escape]
D --> H[JWT, Rate Limit]
E --> I[npm audit, Snyk]
style A fill:#e1f5fe
style B fill:#c8e6c9
style C fill:#c8e6c9
style D fill:#fff9c4
style E fill:#fff9c4
Security is layered: Helmet sets HTTP headers (CSP, X-Frame-Options, HSTS). Input validation rejects malformed data. Parameterized queries prevent injection. Authentication verifies identity. Authorization checks permissions. Rate limiting prevents abuse. Regular audits catch vulnerable dependencies.
Think of API security like a castle defense. Helmet is the outer wall (headers). Input validation is the gate check (who enters). Auth is the inner guards (identification). Authorization is the room keys (access level). Rate limiting is the crowd control at the gate.
Example: Helmet Security Headers
const helmet = require('helmet');
// Default: sets 15 security headers
app.use(helmet());
// Custom configuration
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"]
}
},
frameguard: { action: 'deny' },
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
}));
Example: Input Sanitization and Injection Prevention
const express = require('express');
const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss-clean');
// Prevent NoSQL injection
app.use(mongoSanitize({
replaceWith: '_',
onSanitize: ({ req, key }) => {
console.warn(`Sanitized ${key} in request`);
}
}));
// Prevent XSS attacks
app.use(xss());
// SQL injection prevention with Prisma (parameterized queries)
app.get('/api/users', async (req, res) => {
// Prisma parameterizes queries automatically
const users = await prisma.user.findMany({
where: {
name: { contains: req.query.search } // Safe: parameterized
}
});
res.json({ data: users });
});
Example: Comprehensive Security Middleware
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const cors = require('cors');
const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss-clean');
const hpp = require('hpp');
// Security middleware stack
app.use(helmet());
// CORS
app.use(cors({
origin: process.env.CORS_ORIGIN || 'https://myapp.com',
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400
}));
// Rate limiting
app.use('/api/', rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
message: { error: 'Too many requests' }
}));
// Body parsing with size limits
app.use(express.json({ limit: '10kb' }));
app.use(express.urlencoded({ extended: true, limit: '10kb' }));
// Data sanitization
app.use(mongoSanitize());
app.use(xss());
// Prevent parameter pollution
app.use(hpp({
whitelist: ['price', 'rating', 'category']
}));
// Remove X-Powered-By
app.disable('x-powered-by');
Example: Dependency Security Auditing
# Check for known vulnerabilities
npm audit
# Fix automatically
npm audit fix
# Check for outdated packages
npm outdated
# Use Snyk for continuous monitoring
npx snyk test
npx snyk monitor
Expected output:
$ npm audit
# Run npm install express@4.18.2 to resolve 1 vulnerability
# SEMVER WARNING: Recommended action is a potentially breaking change
┌───────────────┬─────────────────────────────────┐
│ Low │ Prototype Pollution │
├───────────────┼─────────────────────────────────┤
│ Package │ qs │
├───────────────┼─────────────────────────────────┤
│ Dependency │ express > qs │
└───────────────┴─────────────────────────────────┘
Common Mistakes
- Trusting user input — Never trust any input from the client. Validate, sanitize, and escape everything.
- Exposing stack traces in production — Stack traces reveal internal code structure. Return generic 500 errors in production.
- Using vulnerable dependencies — Outdated packages with known vulnerabilities are the most common attack vector. Run npm audit regularly.
- Weak password hashing — Use bcrypt with cost factor 12 or higher. Never use MD5 or SHA1 for passwords.
- Missing rate limiting on auth endpoints — Login endpoints without rate limiting allow brute force attacks. Always rate limit authentication.
Practice Questions
- What security headers does Helmet add?
- How do you prevent NoSQL injection?
- Why should you use parameterized queries?
- What is the purpose of CSP headers?
- Challenge: Perform a security audit on a sample Express API. Identify and fix: missing Helmet headers, weak CORS configuration, no rate limiting on auth, missing input sanitization, exposed X-Powered-By, and outdated dependencies. Run npm audit and fix vulnerabilities.
FAQ
Mini Project
Perform a complete security hardening of a Node.js REST API. Implement: Helmet security headers, strict CORS, input sanitization (NoSQL injection, XSS), parameter pollution prevention, rate limiting on all endpoints, strict rate limiting on auth, dependency auditing and fixes, and a security.txt endpoint for vulnerability reporting.
What's Next
Now start the REST API project in the next lesson on Building REST APIs with Node.js.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro