Security isn't optional – it's foundational. Every API endpoint is a potential attack vector. Here's how to build secure APIs from the ground up.
Authentication Done Right
JWT is popular, but implementation details matter enormously:
javascript
// Secure JWT implementation
const jwt = require('jsonwebtoken');
function generateTokens(user) {
const accessToken = jwt.sign(
{ id: user.id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '15m' } // Short-lived
);
const refreshToken = jwt.sign(
{ id: user.id },
process.env.REFRESH_SECRET,
{ expiresIn: '7d' }
);
return { accessToken, refreshToken };
}Rate Limiting
Protect against brute force and DDoS attacks with intelligent rate limiting:
javascript
const rateLimit = require('express-rate-limit');
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per window
message: { error: 'Too many requests, try again later' },
standardHeaders: true,
legacyHeaders: false,
});
// Stricter limits for auth endpoints
const authLimiter = rateLimit({
windowMs: 60 * 60 * 1000, // 1 hour
max: 5, // 5 failed attempts
skipSuccessfulRequests: true,
});Input Validation
Never trust client input. Validate everything:
javascript
const { body, validationResult } = require('express-validator');
const createUserValidation = [
body('email').isEmail().normalizeEmail(),
body('password')
.isLength({ min: 8 })
.matches(/^(?=.*[A-Z])(?=.*[0-9])/),
body('name').trim().escape().isLength({ min: 2, max: 50 }),
];
function validate(req, res, next) {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
next();
}Security Headers
Set security headers to prevent common attacks:
javascript
const helmet = require('helmet');
app.use(helmet());
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", "data:", "https:"],
}
}));Security Checklist
- Always use HTTPS in production
- Store secrets in environment variables
- Implement proper CORS configuration
- Log security events for monitoring
- Keep dependencies updated
- Use parameterized queries for database operations