Node.js9 min read
Security Best Practices in Node.js
Secure your Node.js application. Learn about common vulnerabilities and how to prevent them.
Sarah Chen
December 19, 2025
0.0k0
Security Best Practices in Node.js
Security isn't optional. Here are the essentials every Node.js developer must know.
1. Use Helmet
npm install helmet
const helmet = require('helmet');
app.use(helmet());
Helmet sets security headers:
- X-Content-Type-Options
- X-Frame-Options
- Content-Security-Policy
- And more...
2. Prevent SQL/NoSQL Injection
// ❌ Vulnerable
const user = await User.findOne({ email: req.body.email });
// ✅ Safe - validate input type
const email = typeof req.body.email === 'string' ? req.body.email : '';
const user = await User.findOne({ email });
// ❌ Raw SQL - vulnerable
db.query(`SELECT * FROM users WHERE id = ${userId}`);
// ✅ Parameterized query
db.query('SELECT * FROM users WHERE id = ?', [userId]);
3. XSS Prevention
// Escape user input in HTML
const escape = require('escape-html');
const safeContent = escape(userInput);
// Use Content-Security-Policy
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"]
}
}));
4. HTTPS Only
// Redirect HTTP to HTTPS
app.use((req, res, next) => {
if (req.headers['x-forwarded-proto'] !== 'https' && process.env.NODE_ENV === 'production') {
return res.redirect(`https://${req.headers.host}${req.url}`);
}
next();
});
// Secure cookies
app.use(session({
cookie: {
secure: true, // HTTPS only
httpOnly: true, // No JavaScript access
sameSite: 'strict' // CSRF protection
}
}));
5. Password Security
const bcrypt = require('bcryptjs');
// Hash password
const hash = await bcrypt.hash(password, 12);
// Verify password
const isValid = await bcrypt.compare(password, hash);
// Password requirements
const isStrongPassword = (password) => {
return password.length >= 8 &&
/[A-Z]/.test(password) &&
/[a-z]/.test(password) &&
/[0-9]/.test(password);
};
6. Rate Limiting
const rateLimit = require('express-rate-limit');
// Prevent brute force
app.use('/api/auth', rateLimit({
windowMs: 15 * 60 * 1000,
max: 5
}));
7. Input Validation
const { body, validationResult } = require('express-validator');
app.post('/api/users', [
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 8 }),
body('name').trim().escape()
], (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Safe to proceed
});
8. Environment Variables
// ❌ Never commit secrets
const apiKey = 'sk-1234567890';
// ✅ Use environment variables
const apiKey = process.env.API_KEY;
// Check required vars at startup
const required = ['DATABASE_URL', 'JWT_SECRET', 'API_KEY'];
required.forEach(key => {
if (!process.env[key]) {
console.error(`Missing: ${key}`);
process.exit(1);
}
});
9. Dependency Security
# Check for vulnerabilities
npm audit
# Fix automatically
npm audit fix
# Update dependencies
npm update
10. Error Handling
// ❌ Leaks info
app.use((err, req, res, next) => {
res.status(500).json({ error: err.stack });
});
// ✅ Safe
app.use((err, req, res, next) => {
console.error(err.stack); // Log for debugging
res.status(500).json({ error: 'Internal server error' });
});
Security Checklist
□ Helmet middleware enabled
□ HTTPS enforced
□ Secure cookie settings
□ Input validation on all routes
□ SQL/NoSQL injection prevention
□ XSS prevention
□ CSRF tokens for forms
□ Rate limiting on sensitive endpoints
□ Strong password hashing (bcrypt)
□ Secrets in environment variables
□ Dependencies regularly updated
□ Error messages don't leak info
Complete Secure Setup
const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const rateLimit = require('express-rate-limit');
const app = express();
// Security middleware
app.use(helmet());
app.use(cors({ origin: process.env.FRONTEND_URL }));
app.use(express.json({ limit: '10kb' })); // Limit body size
app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));
// Trust proxy if behind nginx/load balancer
app.set('trust proxy', 1);
Key Takeaway
Security is layers. Use Helmet, validate all input, hash passwords, rate limit, use HTTPS, keep dependencies updated. Never expose sensitive info in errors. Think like an attacker to defend like a pro.
#Node.js#Security#Best Practices#Intermediate