| name | api-security-hardener |
| description | Hardens API security with rate limiting, input validation, authentication, and protection against common attacks. Use when users request "API security", "secure API", "rate limiting", "input validation", or "API protection". |
API Security Hardener
Implement comprehensive security measures for production APIs.
Core Workflow
- Input validation: Sanitize and validate all input
- Authentication: Secure identity verification
- Authorization: Role-based access control
- Rate limiting: Prevent abuse
- Security headers: HTTP header protection
- Logging & monitoring: Detect threats
Input Validation
Zod Schema Validation
import { z } from 'zod';
export const emailSchema = z.string().email().toLowerCase().trim();
export const passwordSchema = z
.string()
.min(8, 'Password must be at least 8 characters')
.max(128, 'Password too long')
.regex(/[A-Z]/, 'Password must contain uppercase letter')
.regex(/[a-z]/, 'Password must contain lowercase letter')
.regex(/[0-9]/, 'Password must contain number')
.regex(/[^A-Za-z0-9]/, 'Password must contain special character');
export const uuidSchema = z.string().uuid();
export const paginationSchema = z.object({
page: z.coerce.number().int().positive().default(1),
limit: z.coerce.number().int().min(1).max(100).default(20),
sortBy: z.string().optional(),
sortOrder: z.enum(['asc', 'desc']).default('desc'),
});
export const createUserSchema = z.object({
email: emailSchema,
password: passwordSchema,
name: z.string().min(2).max(100).trim(),
});
export const updateUserSchema = createUserSchema.partial().omit({ password: true });
export const sanitizedStringSchema = z.string().transform((val) => {
return val
.replace(/[<>]/g, '')
.replace(/javascript:/gi, '')
.replace(/on\w+=/gi, '')
.trim();
});
Validation Middleware
import { Request, Response, NextFunction } from 'express';
import { z, ZodSchema } from 'zod';
interface ValidationSchemas {
body?: ZodSchema;
query?: ZodSchema;
params?: ZodSchema;
}
export function validate(schemas: ValidationSchemas) {
return async (req: Request, res: Response, next: NextFunction) => {
try {
if (schemas.body) {
req.body = await schemas.body.parseAsync(req.body);
}
if (schemas.query) {
req.query = await schemas.query.parseAsync(req.query);
}
if (schemas.params) {
req.params = await schemas..(req.);
}
();
} (error) {
(error z.) {
res.().({
: ,
: error..( ({
: e..(),
: e.,
})),
});
}
(error);
}
};
}
router.(
,
({ : createUserSchema }),
createUserHandler
);
Rate Limiting
import rateLimit from 'express-rate-limit';
import RedisStore from 'rate-limit-redis';
import Redis from 'ioredis';
const redis = new Redis(process.env.REDIS_URL!);
export const apiLimiter = rateLimit({
store: new RedisStore({
sendCommand: (...args: string[]) => redis.call(...args),
}),
windowMs: 60 * 1000,
max: 100,
message: {
error: 'Too Many Requests',
message: 'Please try again later',
retryAfter: 60,
},
standardHeaders: true,
legacyHeaders: false,
keyGenerator: (req) => {
req.?. || req.;
},
: {
req. === ;
},
});
authLimiter = ({
: ({
: redis.(...args),
}),
: * * ,
: ,
: {
: ,
: ,
},
: ,
});
costLimiter = ({
: * * ,
: ,
: req.?. || req.,
: {
res.().({
: ,
: ,
});
},
});
router.(, {
req. = { ...req., : req.. + };
();
}, costLimiter, handler);
Authentication Middleware
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
interface JWTPayload {
sub: string;
email: string;
role: string;
iat: number;
exp: number;
}
declare global {
namespace Express {
interface Request {
user?: JWTPayload;
}
}
}
export function authenticate(req: Request, res: Response, next: NextFunction) {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
return res.status(401).json({
error: 'Unauthorized',
message: ,
});
}
token = authHeader.();
{
payload = jwt.(token, process..!) ;
(payload. * < .()) {
res.().({
: ,
: ,
});
}
req. = payload;
();
} (error) {
(error jwt.) {
res.().({
: ,
: ,
});
}
(error jwt.) {
res.().({
: ,
: ,
});
}
(error);
}
}
() {
authHeader = req..;
(!authHeader?.()) {
();
}
token = authHeader.();
{
req. = jwt.(token, process..!) ;
} {
}
();
}
Authorization Middleware
import { Request, Response, NextFunction } from 'express';
type Role = 'admin' | 'user' | 'guest';
interface Permission {
resource: string;
actions: string[];
}
const rolePermissions: Record<Role, Permission[]> = {
admin: [
{ resource: '*', actions: ['*'] },
],
user: [
{ resource: 'posts', actions: ['read', 'create', 'update:own', 'delete:own'] },
{ resource: 'comments', actions: ['read', 'create', 'update:own', 'delete:own'] },
{ resource: 'profile', actions: ['read', 'update'] },
],
guest: [
{ resource: 'posts', actions: ['read'] },
{ resource: , : [] },
],
};
() {
{
(!req.) {
res.().({
: ,
: ,
});
}
userRole = req.. ;
(!roles.(userRole)) {
res.().({
: ,
: ,
});
}
();
};
}
() {
{
(!req.) {
res.().({ : });
}
userRole = req.. ;
permissions = rolePermissions[userRole];
hasAccess = permissions.( {
resourceMatch = perm. === || perm. === resource;
actionMatch = perm..() || perm..(action);
resourceMatch && actionMatch;
});
(!hasAccess) {
res.().({
: ,
: ,
});
}
();
};
}
router.(, authenticate, (, ), deletePost);
router.(, authenticate, (), listUsers);
Security Headers
import helmet from 'helmet';
import { Express } from 'express';
export function configureSecurityHeaders(app: Express) {
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", 'data:', 'https:'],
connectSrc: ["'self'", 'https://api.example.com'],
fontSrc: ["'self'"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"],
},
},
crossOriginEmbedderPolicy: false,
crossOriginResourcePolicy: { policy: 'cross-origin' },
}));
app.use((req, res, next) => {
(req..()) {
res.(, );
res.(, );
res.(, );
}
res.(
,
);
();
});
}
SQL Injection Prevention
import { Pool } from 'pg';
const pool = new Pool();
export async function getUserById(id: string) {
const result = await pool.query(
'SELECT * FROM users WHERE id = $1',
[id]
);
return result.rows[0];
}
export async function searchUsers(term: string) {
return prisma.user.findMany({
where: {
OR: [
{ name: { contains: term, mode: 'insensitive' } },
{ email: { contains: term, mode: 'insensitive' } },
],
},
});
}
XSS Prevention
import DOMPurify from 'isomorphic-dompurify';
export function sanitizeHtml(dirty: string): string {
return DOMPurify.sanitize(dirty, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],
ALLOWED_ATTR: ['href', 'target'],
});
}
export function escapeHtml(text: string): string {
const map: Record<string, string> = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
};
return text.replace(/[&<>"']/g, (m) => map[m]);
}
app.(, );
Request Logging
import { Request, Response, NextFunction } from 'express';
import { v4 as uuidv4 } from 'uuid';
export function requestLogger(req: Request, res: Response, next: NextFunction) {
const requestId = req.headers['x-request-id'] as string || uuidv4();
const startTime = Date.now();
res.setHeader('X-Request-ID', requestId);
req.requestId = requestId;
console.log(JSON.stringify({
type: 'request',
requestId,
method: req.method,
path: req.path,
query: req.query,
ip: req.ip,
userAgent: req.headers['user-agent'],
: req.?.,
: ().(),
}));
res.(, {
duration = .() - startTime;
.(.({
: ,
requestId,
: req.,
: req.,
: res.,
duration,
: req.?.,
: ().(),
}));
(res. === || res. === ) {
.(.({
: ,
: ,
requestId,
: req.,
: req.,
: res.,
}));
}
});
();
}
Error Handling
import { Request, Response, NextFunction } from 'express';
export class AppError extends Error {
constructor(
public statusCode: number,
public message: string,
public code?: string
) {
super(message);
this.name = 'AppError';
}
}
export function errorHandler(
err: Error,
req: Request,
res: Response,
next: NextFunction
) {
console.error({
type: 'error',
requestId: req.requestId,
error: err.message,
stack: process.env.NODE_ENV === 'development' ? err.stack : undefined,
});
(err ) {
res.(err.).({
: err.,
: err.,
: err.,
});
}
res.().({
: ,
: ,
: req.,
});
}
Best Practices
- Validate everything: Never trust client input
- Use parameterized queries: Prevent SQL injection
- Sanitize output: Prevent XSS
- Rate limit: Protect against abuse
- Log everything: Enable audit trails
- Use HTTPS: Always encrypt in transit
- Minimal responses: Don't leak information
- Update dependencies: Patch vulnerabilities
Output Checklist
Every API security implementation should include: