Skip to content
Plurafy

Security Best Practices

Security is paramount in production applications. Learn essential security practices for protecting Node.js applications and user data.

Implement comprehensive security measures to protect against common vulnerabilities and attacks in Node.js applications.

  • Injection Attacks: SQL injection, command injection
  • Authentication: Weak password handling, token issues
  • Authorization: Insufficient permission checks
  • Data Exposure: Sensitive data leaks
  • Dependencies: Vulnerable packages
  • CORS: Cross-origin request issues
  • Input Validation: Sanitize and validate inputs
  • Encryption: Protect data in transit and at rest
  • Authentication: Secure user verification
  • Authorization: Fine-grained access control
  • Auditing: Track security-related events
const express = require('express');
const validator = require('express-validator');
const DOMPurify = require('isomorphic-dompurify');
const app = express();
// Validation middleware
const validateUser = [
validator.body('email').isEmail().normalizeEmail(),
validator.body('username')
.isLength({ min: 3, max: 20 })
.matches(/^[a-zA-Z0-9_]+$/),
validator.body('password')
.isLength({ min: 12 })
.matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])/),
];
app.post('/register', validateUser, (req, res) => {
const errors = validator.validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Sanitize inputs
const cleanEmail = DOMPurify.sanitize(req.body.email);
const cleanUsername = DOMPurify.sanitize(req.body.username);
// Process registration
res.json({ message: 'Registration successful' });
});
// SQL injection prevention
app.get('/users/:id', (req, res) => {
const id = parseInt(req.params.id);
if (isNaN(id)) {
return res.status(400).json({ error: 'Invalid ID' });
}
// Use parameterized queries
const query = 'SELECT * FROM users WHERE id = $1';
database.query(query, [id], (err, result) => {
if (err) return res.status(500).json({ error: err.message });
res.json(result.rows);
});
});
const bcrypt = require('bcrypt');
const crypto = require('crypto');
class PasswordManager {
async hashPassword(password) {
const saltRounds = 12; // Bcrypt cost factor
return bcrypt.hash(password, saltRounds);
}
async verifyPassword(password, hash) {
return bcrypt.compare(password, hash);
}
generateTemporaryPassword() {
return crypto.randomBytes(16).toString('hex');
}
validatePasswordStrength(password) {
const checks = {
length: password.length >= 12,
uppercase: /[A-Z]/.test(password),
lowercase: /[a-z]/.test(password),
numbers: /\d/.test(password),
special: /[@$!%*?&]/.test(password),
};
return {
isStrong: Object.values(checks).every(v => v),
checks,
};
}
}
// Usage
const passwordManager = new PasswordManager();
// During registration
app.post('/register', async (req, res) => {
const { password } = req.body;
const strength = passwordManager.validatePasswordStrength(password);
if (!strength.isStrong) {
return res.status(400).json({
error: 'Password does not meet requirements',
checks: strength.checks,
});
}
const hash = await passwordManager.hashPassword(password);
// Store hash in database
});
// During login
app.post('/login', async (req, res) => {
const { email, password } = req.body;
const user = await database.users.findByEmail(email);
if (!user) {
// Don't reveal if email exists
return res.status(401).json({ error: 'Invalid credentials' });
}
const isValid = await passwordManager.verifyPassword(password, user.passwordHash);
if (!isValid) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Issue JWT token
res.json({ token: generateToken(user) });
});
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const app = express();
// Security headers
app.use(helmet());
// CORS configuration
const corsOptions = {
origin: process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3000'],
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
maxAge: 3600,
};
app.use(cors(corsOptions));
// HTTPS enforcement
app.use((req, res, next) => {
if (process.env.NODE_ENV === 'production' && req.header('x-forwarded-proto') !== 'https') {
return res.redirect(301, `https://${req.header('host')}${req.url}`);
}
next();
});
// Content Security Policy
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", 'https:'],
},
}));
// Prevent clickjacking
app.use(helmet.frameguard({ action: 'deny' }));
const jwt = require('jsonwebtoken');
class TokenManager {
constructor(secret = process.env.JWT_SECRET, expiresIn = '24h') {
this.secret = secret;
this.expiresIn = expiresIn;
}
generateToken(payload, options = {}) {
const tokenPayload = {
...payload,
iat: Math.floor(Date.now() / 1000),
};
return jwt.sign(tokenPayload, this.secret, {
expiresIn: this.expiresIn,
...options,
});
}
verifyToken(token) {
try {
return jwt.verify(token, this.secret);
} catch (error) {
throw new Error(`Invalid token: ${error.message}`);
}
}
generateRefreshToken(userId) {
return jwt.sign({ userId, type: 'refresh' }, this.secret, {
expiresIn: '7d',
});
}
revokeToken(token) {
// Store revoked tokens in Redis or database
const decoded = jwt.decode(token);
const ttl = decoded.exp - Math.floor(Date.now() / 1000);
if (ttl > 0) {
// Store in cache with TTL
cache.set(`revoked:${token}`, 'true', ttl);
}
}
isTokenRevoked(token) {
return cache.get(`revoked:${token}`) !== null;
}
}
// Middleware
function authMiddleware(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
const tokenManager = new TokenManager();
try {
if (tokenManager.isTokenRevoked(token)) {
return res.status(401).json({ error: 'Token has been revoked' });
}
const payload = tokenManager.verifyToken(token);
req.user = payload;
next();
} catch (error) {
res.status(401).json({ error: error.message });
}
}
Terminal window
# Check for vulnerabilities
npm audit
# Fix vulnerabilities
npm audit fix
# Check for outdated packages
npm outdated
# Update packages
npm update
const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const redis = require('redis');
const client = redis.createClient();
const limiter = rateLimit({
store: new RedisStore({
client: client,
prefix: 'rate-limit:',
}),
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per windowMs
message: 'Too many requests from this IP',
standardHeaders: true,
legacyHeaders: false,
});
// Apply globally
app.use(limiter);
// Stricter limit for auth
const authLimiter = rateLimit({
store: new RedisStore({
client: client,
prefix: 'auth-rate-limit:',
}),
windowMs: 15 * 60 * 1000,
max: 5, // 5 attempts
skipSuccessfulRequests: true,
});
app.post('/login', authLimiter, (req, res) => {
// Login logic
});
  1. Input Validation

    • Validate all inputs
    • Sanitize before processing
    • Use parameterized queries
    • Implement allowlists
  2. Authentication

    • Use strong password policies
    • Implement MFA
    • Secure token storage
    • Handle token refresh properly
  3. Authorization

    • Implement least privilege
    • Check permissions on every request
    • Use role-based access control
    • Audit access attempts
  4. Data Protection

    • Encrypt sensitive data
    • Use HTTPS/TLS
    • Implement secure headers
    • Handle PII carefully
  5. Dependency Management

    • Keep dependencies updated
    • Run security audits regularly
    • Remove unused packages
    • Review package advisories
  • User Authentication: Secure login systems
  • API Protection: Rate limiting and validation
  • Data Encryption: Protecting sensitive information
  • Compliance: Meeting GDPR, PCI-DSS requirements
  • Threat Detection: Identifying suspicious activity