"""安全响应头中间件 — HSTS / X-Frame-Options / X-Content-Type-Options 等。""" from __future__ import annotations from fastapi import Request, Response from starlette.middleware.base import BaseHTTPMiddleware from starlette.types import ASGIApp from app.core.config import settings class SecurityHeadersMiddleware(BaseHTTPMiddleware): """为 HTTP 响应注入安全加固头。 - Strict-Transport-Security (HSTS):仅在 HTTPS 且启用时注入 - X-Content-Type-Options: nosniff - X-Frame-Options: DENY - X-XSS-Protection: 1; mode=block - Referrer-Policy: strict-origin-when-cross-origin - Permissions-Policy: 限制敏感 API(摄像头/麦克风/定位) """ def __init__(self, app: ASGIApp) -> None: super().__init__(app) async def dispatch(self, request: Request, call_next) -> Response: response = await call_next(request) # HSTS:仅在 HTTPS 且显式启用时注入(开发环境 http 不应发送 HSTS) if settings.HSTS_ENABLED and request.url.scheme == "https": hsts = f"max-age={settings.HSTS_MAX_AGE}" if settings.HSTS_INCLUDE_SUBDOMAINS: hsts += "; includeSubDomains" response.headers["Strict-Transport-Security"] = hsts # 通用安全头(对所有响应安全) response.headers.setdefault("X-Content-Type-Options", "nosniff") response.headers.setdefault("X-Frame-Options", "DENY") response.headers.setdefault("X-XSS-Protection", "1; mode=block") response.headers.setdefault("Referrer-Policy", "strict-origin-when-cross-origin") response.headers.setdefault( "Permissions-Policy", "camera=(), microphone=(), geolocation=()", ) return response