API Security Threats
APIs are the backbone of modern applications and a prime target for attackers. The OWASP API Security Top 10 highlights the most critical risks.
Authentication
OAuth 2.0 with PKCE
// Client-side PKCE flow
async function initiateAuth() {
const codeVerifier = generateRandomString(128);
const codeChallenge = await sha256(codeVerifier);
// Store verifier for token exchange
sessionStorage.setItem('code_verifier', codeVerifier);
const authUrl = new URL('https://auth.example.com/authorize');
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('client_id', CLIENT_ID);
authUrl.searchParams.set('redirect_uri', REDIRECT_URI);
authUrl.searchParams.set('scope', 'openid profile api');
authUrl.searchParams.set('code_challenge', codeChallenge);
authUrl.searchParams.set('code_challenge_method', 'S256');
window.location.href = authUrl.toString();
}JWT Validation
import jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';
const client = jwksClient({
jwksUri: 'https://auth.example.com/.well-known/jwks.json',
cache: true,
rateLimit: true
});
async function validateToken(token) {
const decoded = jwt.decode(token, { complete: true });
const key = await client.getSigningKey(decoded.header.kid);
return jwt.verify(token, key.getPublicKey(), {
algorithms: ['RS256'],
issuer: 'https://auth.example.com',
audience: 'your-api-audience'
});
}Authorization
Object-Level Authorization (BOLA Prevention)
// Always verify resource ownership
async function getOrder(req, res) {
const order = await Order.findById(req.params.orderId);
if (!order) {
return res.status(404).json({ error: 'Order not found' });
}
// CRITICAL: Verify the user owns this resource
if (order.userId !== req.user.id && !req.user.isAdmin) {
return res.status(403).json({ error: 'Access denied' });
}
res.json(order);
}Function-Level Authorization
// Role-based access control middleware
function requireRole(...roles) {
return (req, res, next) => {
if (!req.user || !roles.some(role => req.user.roles.includes(role))) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
}
app.delete('/users/:id', requireRole('admin'), deleteUser);
app.post('/orders', requireRole('user', 'admin'), createOrder);Input Validation
import { z } from 'zod';
const CreateUserSchema = z.object({
email: z.string().email().max(255),
name: z.string().min(1).max(100).regex(/^[a-zA-Z\s]+$/),
age: z.number().int().min(18).max(120).optional()
});
function validateBody(schema) {
return (req, res, next) => {
const result = schema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
error: 'Validation failed',
details: result.error.issues
});
}
req.body = result.data; // Use sanitized data
next();
};
}
app.post('/users', validateBody(CreateUserSchema), createUser);Rate Limiting
import rateLimit from 'express-rate-limit';
import RedisStore from 'rate-limit-redis';
// General API rate limit
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100,
standardHeaders: true,
store: new RedisStore({ client: redisClient }),
keyGenerator: (req) => req.user?.id || req.ip
});
// Strict limit for sensitive endpoints
const authLimiter = rateLimit({
windowMs: 60 * 60 * 1000, // 1 hour
max: 5,
message: 'Too many login attempts'
});
app.use('/api/', apiLimiter);
app.post('/auth/login', authLimiter, login);SQL Injection Prevention
// WRONG - vulnerable to SQL injection
const query = `SELECT * FROM users WHERE email = '${email}'`;
// CORRECT - parameterized query
const result = await db.query(
'SELECT * FROM users WHERE email = $1',
[email]
);
// CORRECT - ORM with automatic escaping
const user = await User.findOne({ where: { email } });Security Headers
import helmet from 'helmet';
app.use(helmet());
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", 'data:', 'https:'],
connectSrc: ["'self'", 'https://api.example.com']
}
}));Logging & Monitoring
// Security event logging
function logSecurityEvent(event) {
const log = {
timestamp: new Date().toISOString(),
type: event.type,
severity: event.severity,
userId: event.userId,
ip: event.ip,
details: event.details
};
securityLogger.log(log);
if (event.severity === 'critical') {
alertService.notify(log);
}
}Conclusion
API security requires defense in depth. Implement authentication, authorization, validation, and monitoring. Regularly audit against OWASP API Security Top 10.