Team Ai
Apppublic

LetsTryGPT/agent4-implementation

sourceHugging Faceupdated 11mo agoView on Hugging Face
0likes
security.ts243 linesDownload Raw Back to middleware
1import { Request, Response, NextFunction } from 'express';2import { logger } from '../utils';3 4/**5 * Enterprise Security Headers Middleware6 * Implements OWASP recommended security headers7 *8 * Reference: https://owasp.org/www-project-secure-headers/9 */10export function securityHeaders(req: Request, res: Response, next: NextFunction): void {11  // Prevent clickjacking attacks12  res.setHeader('X-Frame-Options', 'DENY');13 14  // Enable XSS protection in legacy browsers15  res.setHeader('X-XSS-Protection', '1; mode=block');16 17  // Prevent MIME type sniffing18  res.setHeader('X-Content-Type-Options', 'nosniff');19 20  // Control referrer information21  res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');22 23  // Content Security Policy - Strict policy for API server24  res.setHeader(25    'Content-Security-Policy',26    "default-src 'none'; frame-ancestors 'none'; base-uri 'none'"27  );28 29  // Permissions Policy (formerly Feature-Policy)30  res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=(), payment=()');31 32  // Strict Transport Security (HSTS) - Only if behind HTTPS33  // 2 years max-age with preload for production34  if (req.secure || req.headers['x-forwarded-proto'] === 'https') {35    res.setHeader('Strict-Transport-Security', 'max-age=63072000; includeSubDomains; preload');36  }37 38  // Remove X-Powered-By header to hide Express39  res.removeHeader('X-Powered-By');40 41  next();42}43 44/**45 * Request sanitization middleware46 * Validates and sanitizes common attack vectors47 */48export function sanitizeRequest(req: Request, res: Response, next: NextFunction): void {49  const requestId = req.headers['x-request-id'] as string;50 51  // Check for suspicious patterns in headers52  const suspiciousHeaderPatterns = [53    /<script/i,54    /javascript:/i,55    /onerror=/i,56    /onclick=/i,57    /\.\.[\\/]/i, // Path traversal (.. followed by / or \)58    /%2e%2e%2f/i, // URL encoded path traversal (../)59    /%2e%2e%5c/i, // URL encoded path traversal (..\)60  ];61 62  // Skip infrastructure headers from security checks63  const infrastructureHeaders = new Set([64    'x-vercel-id',65    'x-vercel-cache',66    'x-forwarded-for',67    'x-forwarded-proto',68    'x-forwarded-host',69    'x-real-ip',70    'via',71    'forwarded',72  ]);73 74  for (const [headerName, headerValue] of Object.entries(req.headers)) {75    // Skip infrastructure headers76    if (infrastructureHeaders.has(headerName.toLowerCase())) {77      continue;78    }79 80    if (typeof headerValue === 'string') {81      for (const pattern of suspiciousHeaderPatterns) {82        if (pattern.test(headerValue)) {83          logger.warn('Suspicious header pattern detected', {84            requestId,85            header: headerName,86            pattern: pattern.toString(),87            ip: req.ip,88          });89 90          res.status(400).json({91            success: false,92            error: 'Invalid request headers',93            requestId,94          });95          return;96        }97      }98    }99  }100 101  // Check for SQL injection patterns in query parameters102  if (req.query && Object.keys(req.query).length > 0) {103    const sqlInjectionPatterns = [104      /(\bOR\b|\bAND\b).*=.*=/i,105      /UNION.*SELECT/i,106      /DROP\s+TABLE/i,107      /INSERT\s+INTO/i,108      /DELETE\s+FROM/i,109      /UPDATE.*SET/i,110      /--/,111      /;.*--/,112      /\/\*/,113    ];114 115    const queryString = JSON.stringify(req.query);116    for (const pattern of sqlInjectionPatterns) {117      if (pattern.test(queryString)) {118        logger.warn('Potential SQL injection attempt detected', {119          requestId,120          query: req.query,121          pattern: pattern.toString(),122          ip: req.ip,123        });124 125        res.status(400).json({126          success: false,127          error: 'Invalid query parameters',128          requestId,129        });130        return;131      }132    }133  }134 135  next();136}137 138/**139 * IP validation and rate limiting bypass prevention140 * Ensures X-Forwarded-For header is not spoofed141 */142export function validateClientIP(req: Request, _res: Response, next: NextFunction): void {143  const requestId = req.headers['x-request-id'] as string;144 145  // Log actual client IP for security monitoring146  const clientIP = req.ip || req.socket.remoteAddress || 'unknown';147  const forwardedFor = req.headers['x-forwarded-for'];148 149  // Detect potential IP spoofing attempts150  if (forwardedFor && typeof forwardedFor === 'string') {151    const ips = forwardedFor.split(',').map((ip) => ip.trim());152    if (ips.length > 5) {153      logger.warn('Suspicious X-Forwarded-For chain detected', {154        requestId,155        chain: ips,156        clientIP,157      });158    }159  }160 161  // Store validated IP for rate limiting162  req.headers['x-client-ip'] = clientIP;163 164  next();165}166 167/**168 * Request timeout middleware169 * Prevents slowloris and similar DoS attacks170 */171export function requestTimeout(timeoutMs: number = 30000) {172  return function (req: Request, res: Response, next: NextFunction): void {173    const requestId = req.headers['x-request-id'] as string;174 175    // Set timeout for the request176    const timer = setTimeout(() => {177      if (!res.headersSent) {178        logger.warn('Request timeout', {179          requestId,180          url: req.url,181          method: req.method,182          timeout: timeoutMs,183        });184 185        res.status(408).json({186          success: false,187          error: 'Request timeout',188          requestId,189        });190      }191    }, timeoutMs);192 193    // Clear timeout when response is sent194    res.on('finish', () => clearTimeout(timer));195    res.on('close', () => clearTimeout(timer));196 197    next();198  };199}200 201/**202 * JSON payload validation203 * Prevents JSON injection and oversized payloads204 */205export function validateJSONPayload(req: Request, res: Response, next: NextFunction): void {206  const requestId = req.headers['x-request-id'] as string;207 208  // Check content-type for JSON endpoints209  if (req.method === 'POST' || req.method === 'PUT' || req.method === 'PATCH') {210    const contentType = req.headers['content-type'];211 212    if (contentType && !contentType.includes('application/json')) {213      logger.warn('Invalid content-type for JSON endpoint', {214        requestId,215        contentType,216        method: req.method,217        url: req.url,218      });219 220      res.status(415).json({221        success: false,222        error: 'Unsupported Media Type. Expected application/json',223        requestId,224      });225      return;226    }227  }228 229  next();230}231 232/**233 * Comprehensive security middleware suite234 * Apply all security measures in correct order235 */236export const securityMiddleware = [237  securityHeaders,238  validateClientIP,239  sanitizeRequest,240  validateJSONPayload,241  requestTimeout(30000), // 30 second timeout for LLM requests242];243