What are the key security best practices in Node.js?
Node.js Security Best Practices
Security is critical in any backend application. Node.js has specific attack vectors that every developer should understand and defend against.
1. Prevent Injection Attacks
SQL Injection
js
// β Vulnerable β string concatenation
const query = `SELECT * FROM users WHERE id = '${req.params.id}'`;
// β
Safe β parameterized queries
const result = await db.query('SELECT * FROM users WHERE id = $1', [req.params.id]);NoSQL Injection (MongoDB)
js
// β Vulnerable β user input can be an object
db.users.find({ username: req.body.username, password: req.body.password });
// Attacker sends: { "password": { "$gt": "" } }
// β
Safe β validate and sanitize input
const username = String(req.body.username);
const password = String(req.body.password);
db.users.find({ username, password: hashPassword(password) });Command Injection
js
// β Vulnerable
const { exec } = require('child_process');
exec(`ls ${req.query.dir}`); // dir = "; rm -rf /"
// β
Safe β use execFile with array arguments
const { execFile } = require('child_process');
execFile('ls', [req.query.dir]);2. Input Validation
Always validate and sanitize user input:
js
const Joi = require('joi');
const schema = Joi.object({
email: Joi.string().email().required(),
password: Joi.string().min(8).max(128).required(),
age: Joi.number().integer().min(18).max(120)
});
const { error, value } = schema.validate(req.body);
if (error) return res.status(400).json({ error: error.details });3. Authentication & Authorization
| Practice | Implementation |
|---|---|
| Hash passwords | bcrypt with salt rounds β₯ 12 |
| Use JWT carefully | Short expiry, httpOnly cookies, refresh tokens |
| Rate limit login | Prevent brute force (express-rate-limit) |
| Use HTTPS | Always in production |
js
const bcrypt = require('bcrypt');
// Hash
const hash = await bcrypt.hash(password, 12);
// Verify
const isValid = await bcrypt.compare(password, hash);4. HTTP Security Headers
Use helmet to set security headers:
js
const helmet = require('helmet');
app.use(helmet());
// Sets headers like:
// X-Content-Type-Options: nosniff
// X-Frame-Options: DENY
// Strict-Transport-Security: max-age=31536000
// Content-Security-Policy: default-src 'self'5. Dependency Security
bash
# Check for known vulnerabilities
npm audit
# Fix automatically where possible
npm audit fix
# Use Snyk for deeper analysis
npx snyk test6. Prevent Denial of Service (DoS)
js
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: 'Too many requests'
});
app.use('/api/', limiter);Protect against ReDoS (Regular Expression DoS):
js
// β Vulnerable regex β catastrophic backtracking
const regex = /^(a+)+$/;
regex.test('aaaaaaaaaaaaaaaaaaaaaaaaaaaaX'); // Hangs!
// β
Safe β use safe-regex or re2
const RE2 = require('re2');
const regex = new RE2('^(a+)+$');7. Secure Environment Variables
js
// β Never hardcode secrets
const secret = 'my-secret-key';
// β
Use environment variables
const secret = process.env.JWT_SECRET;
// β
Validate at startup
if (!process.env.JWT_SECRET) {
throw new Error('JWT_SECRET is required');
}Security Checklist
| Category | Action |
|---|---|
| Input | Validate all user input (Joi, class-validator) |
| SQL/NoSQL | Use parameterized queries |
| Auth | Hash passwords with bcrypt, use JWT properly |
| Headers | Use helmet middleware |
| HTTPS | Enforce TLS in production |
| Dependencies | Run npm audit regularly |
| Rate limiting | Protect endpoints from abuse |
| Secrets | Use env vars, never commit to git |
| Logging | Log security events, never log secrets |
| CORS | Configure strict origin policies |
Remember: Security is not a feature you add once β it's a mindset. Validate input, sanitize output, use least privilege, keep dependencies updated, and monitor for anomalies.
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.