Initial Commit
This commit is contained in:
85
server/middleware.ts
Normal file
85
server/middleware.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import helmet from 'helmet';
|
||||
import rateLimit from 'express-rate-limit';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { config } from './config';
|
||||
import { securityLogger } from './logger';
|
||||
|
||||
// Security headers middleware
|
||||
export const securityHeaders = helmet({
|
||||
contentSecurityPolicy: {
|
||||
directives: {
|
||||
defaultSrc: ["'self'"],
|
||||
styleSrc: ["'self'", "'unsafe-inline'"],
|
||||
scriptSrc: ["'self'"],
|
||||
imgSrc: ["'self'", "data:", "blob:"],
|
||||
connectSrc: ["'self'"],
|
||||
fontSrc: ["'self'"],
|
||||
objectSrc: ["'none'"],
|
||||
mediaSrc: ["'self'"],
|
||||
frameSrc: ["'none'"],
|
||||
},
|
||||
},
|
||||
crossOriginEmbedderPolicy: false, // Req. for Socket.io
|
||||
hsts: {
|
||||
maxAge: 31536000,
|
||||
includeSubDomains: true,
|
||||
preload: true
|
||||
}
|
||||
});
|
||||
|
||||
// Rate limiting
|
||||
export const createRateLimit = rateLimit({
|
||||
windowMs: config.rateLimit.windowMs,
|
||||
max: config.rateLimit.maxRequests,
|
||||
message: {
|
||||
error: 'Too many requests from this IP, please try again later.',
|
||||
retryAfter: Math.ceil(config.rateLimit.windowMs / 1000)
|
||||
},
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
handler: (req: Request, res: Response) => {
|
||||
securityLogger.rateLimitExceeded(
|
||||
req.ip || req.connection.remoteAddress || 'unknown',
|
||||
req.path
|
||||
);
|
||||
res.status(429).json({
|
||||
error: 'Too many requests from this IP, please try again later.',
|
||||
retryAfter: Math.ceil(config.rateLimit.windowMs / 1000)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Request logging
|
||||
export const requestLogger = (req: Request, res: Response, next: NextFunction) => {
|
||||
next();
|
||||
};
|
||||
|
||||
// Sanitize input
|
||||
export const sanitizeInput = (req: Request, res: Response, next: NextFunction) => {
|
||||
// Recursively sanitize string values
|
||||
const sanitizeValue = (value: any): any => {
|
||||
if (typeof value === 'string') {
|
||||
return value
|
||||
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '') // Remove script tags
|
||||
.replace(/<[^>]*>/g, '') // Remove HTML tags
|
||||
.trim();
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(sanitizeValue);
|
||||
}
|
||||
if (value && typeof value === 'object') {
|
||||
const sanitized: any = {};
|
||||
for (const [key, val] of Object.entries(value)) {
|
||||
sanitized[key] = sanitizeValue(val);
|
||||
}
|
||||
return sanitized;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
req.body = sanitizeValue(req.body);
|
||||
req.query = sanitizeValue(req.query);
|
||||
req.params = sanitizeValue(req.params);
|
||||
|
||||
next();
|
||||
};
|
||||
Reference in New Issue
Block a user