""" Security Headers Middleware Implements comprehensive security headers for FastAPI application """ import os from typing import Callable from fastapi import Request, Response from starlette.middleware.base import BaseHTTPMiddleware class SecurityHeadersMiddleware(BaseHTTPMiddleware): """Middleware to add security headers to all responses""" def __init__(self, app, is_development: bool = False): super().__init__(app) self.is_development = ( is_development or os.getenv("ENVIRONMENT", "development").lower() == "development" ) async def dispatch(self, request: Request, call_next: Callable) -> Response: response = await call_next(request) # Add security headers based on environment if self.is_development: self._add_development_headers(response) else: self._add_production_headers(response) return response def _add_development_headers(self, response: Response): """Add security headers for development environment""" headers = { # Content Security Policy (relaxed for development) "Content-Security-Policy": ( "default-src 'self'; " "script-src 'self' 'unsafe-inline' 'unsafe-eval'; " "style-src 'self' 'unsafe-inline'; " "img-src 'self' data: blob: https:; " "font-src 'self' https:; " "connect-src 'self' wss://localhost:* ws://localhost:* ws://127.0.0.1:* wss://127.0.0.1:*; " "frame-ancestors 'self'; " "base-uri 'self'; " "form-action 'self'; " "media-src 'self'; " "object-src 'none'; " "worker-src 'self'; " "manifest-src 'self'" ), # Prevent clickjacking "X-Frame-Options": "DENY", # Prevent MIME type sniffing "X-Content-Type-Options": "nosniff", # Enable XSS protection "X-XSS-Protection": "1; mode=block", # Referrer Policy "Referrer-Policy": "strict-origin-when-cross-origin", # Permissions Policy "Permissions-Policy": ( "geolocation=(), " "microphone=(), " "camera=(), " "payment=(), " "usb=(), " "magnetometer=(), " "gyroscope=(), " "accelerometer=(), " "autoplay=(self), " "fullscreen=(self), " "payment=()" ), # Additional security headers "X-DNS-Prefetch-Control": "off", "X-Download-Options": "noopen", "X-Permitted-Cross-Domain-Policies": "none", } for header, value in headers.items(): response.headers[header] = value def _add_production_headers(self, response: Response): """Add security headers for production environment""" headers = { # Content Security Policy (strict for production) "Content-Security-Policy": ( "default-src 'self'; " "script-src 'self'; " "style-src 'self'; " "img-src 'self' data: https:; " "font-src 'self' https:; " "connect-src 'self'; " "frame-ancestors 'self'; " "base-uri 'self'; " "form-action 'self'; " "media-src 'self'; " "object-src 'none'; " "worker-src 'self'; " "manifest-src 'self'; " "block-all-mixed-content" ), # Prevent clickjacking "X-Frame-Options": "DENY", # Prevent MIME type sniffing "X-Content-Type-Options": "nosniff", # Enable XSS protection "X-XSS-Protection": "1; mode=block", # Strict Transport Security (HTTPS only) "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload", # Referrer Policy "Referrer-Policy": "strict-origin-when-cross-origin", # Permissions Policy "Permissions-Policy": ( "geolocation=(), " "microphone=(), " "camera=(), " "payment=(), " "usb=(), " "magnetometer=(), " "gyroscope=(), " "accelerometer=(), " "autoplay=(self), " "fullscreen=(self), " "payment=()" ), # Cross-Origin policies "Cross-Origin-Embedder-Policy": "require-corp", "Cross-Origin-Resource-Policy": "same-origin", "Cross-Origin-Opener-Policy": "same-origin", # Additional security headers "X-DNS-Prefetch-Control": "off", "X-Download-Options": "noopen", "X-Permitted-Cross-Domain-Policies": "none", } for header, value in headers.items(): response.headers[header] = value # Factory function to create middleware def create_security_headers_middleware( is_development: bool = False, ) -> SecurityHeadersMiddleware: """Create security headers middleware with environment configuration""" return SecurityHeadersMiddleware(is_development=is_development)