Learn how to secure your APIs with modern authentication, authorization, and protection mechanisms.
1. JWT Authentication Implementation
const jwt = require('jsonwebtoken');
const express = require('express');
const app = express();
// Secret key (store in environment variable)
const JWT_SECRET = process.env.JWT_SECRET;
// Generate JWT token
function generateToken(user) {
return jwt.sign(
{
userId: user.id,
role: user.role
},
JWT_SECRET,
{
expiresIn: '1h',
algorithm: 'HS256'
}
);
}
// JWT middleware
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
jwt.verify(token, JWT_SECRET, (err, user) => {
if (err) {
return res.status(403).json({ error: 'Invalid token' });
}
req.user = user;
next();
});
}
// Protected route
app.get('/api/protected', authenticateToken, (req, res) => {
res.json({ message: 'Access granted', user: req.user });
});
Example Request/Response
# Request
GET /api/protected
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# Response
{
"message": "Access granted",
"user": {
"userId": "123",
"role": "admin"
}
}
2. Rate Limiting Implementation
const rateLimit = require('express-rate-limit');
const Redis = require('ioredis');
const redis = new Redis();
// Rate limiting middleware
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: 'Too many requests from this IP, please try again later',
handler: async (req, res) => {
const ip = req.ip;
const key = `rate_limit:${ip}`;
// Track blocked requests
await redis.incr(`blocked:${ip}`);
await redis.expire(`blocked:${ip}`, 3600);
res.status(429).json({
error: 'Too many requests',
retryAfter: res.getHeader('Retry-After')
});
}
});
// Apply to all routes
app.use(apiLimiter);
// Stricter limits for sensitive endpoints
const sensitiveLimiter = rateLimit({
windowMs: 60 * 60 * 1000, // 1 hour
max: 5,
message: 'Too many attempts, please try again later'
});
app.post('/api/reset-password', sensitiveLimiter, (req, res) => {
// Password reset logic
});
3. Input Validation and Sanitization
const { body, validationResult } = require('express-validator');
const sanitizeHtml = require('sanitize-html');
// Validation middleware
const validateUserInput = [
body('email').isEmail().normalizeEmail(),
body('password')
.isLength({ min: 8 })
.matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/),
body('name')
.trim()
.isLength({ min: 2, max: 50 })
.customSanitizer(value => sanitizeHtml(value)),
(req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
next();
}
];
// Usage
app.post('/api/users', validateUserInput, (req, res) => {
// Process validated input
const { email, password, name } = req.body;
// Create user...
});
4. API Key Management
const crypto = require('crypto');
class ApiKeyManager {
constructor() {
this.keys = new Map();
}
generateKey(permissions) {
const key = crypto.randomBytes(32).toString('hex');
const hash = crypto.createHash('sha256').update(key).digest('hex');
this.keys.set(hash, {
permissions,
createdAt: Date.now(),
lastUsed: null
});
return key;
}
validateKey(key, requiredPermission) {
const hash = crypto.createHash('sha256').update(key).digest('hex');
const keyData = this.keys.get(hash);
if (!keyData) return false;
keyData.lastUsed = Date.now();
return keyData.permissions.includes(requiredPermission);
}
revokeKey(key) {
const hash = crypto.createHash('sha256').update(key).digest('hex');
return this.keys.delete(hash);
}
}
// Usage
const apiKeyManager = new ApiKeyManager();
const key = apiKeyManager.generateKey(['read', 'write']);
// Middleware
function validateApiKey(permission) {
return (req, res, next) => {
const apiKey = req.headers['x-api-key'];
if (!apiKey || !apiKeyManager.validateKey(apiKey, permission)) {
return res.status(401).json({ error: 'Invalid API key' });
}
next();
};
}
⚠️ Common API Security Mistakes
- Exposing sensitive data in error messages
- Not implementing rate limiting
- Weak or missing authentication
- Insufficient input validation
- Not using HTTPS
✅ API Security Best Practices
- Use JWT or OAuth 2.0 for authentication
- Implement proper rate limiting
- Validate and sanitize all input
- Use API keys for third-party access
- Enable CORS with proper configuration
API Security Checklist
- ✅ Implement proper authentication
- ✅ Add rate limiting
- ✅ Validate all input
- ✅ Use HTTPS
- ✅ Implement proper error handling
- ✅ Enable CORS with restrictions
- ✅ Log security events
- ✅ Regular security audits