Security Best Practices in Node.js
Secure your Node.js application. Learn about common vulnerabilities and how to prevent them.
Security Best Practices in Node.js
Security isn't optional. Here are the essentials every Node.js developer must know.
1. Use Helmet
```bash npm install helmet ```
```javascript 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
```javascript // ❌ 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
```javascript // 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
```javascript // 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
```javascript 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
```javascript const rateLimit = require('express-rate-limit');
// Prevent brute force app.use('/api/auth', rateLimit({ windowMs: 15 * 60 * 1000, max: 5 })); ```
7. Input Validation
```javascript 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
```javascript // ❌ 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
```bash # Check for vulnerabilities npm audit
Fix automatically npm audit fix
Update dependencies npm update ```
10. Error Handling
```javascript // ❌ 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
```javascript 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.