morphos / backend /app /security /headers.py
Jose Salazar
A帽adir el stack nuevo: Vite/TypeScript + FastAPI
70e641d
Raw
History Blame Contribute Delete
1.71 kB
"""Middleware de cabeceras de seguridad.
A帽ade CSP, HSTS (en prod), X-Content-Type-Options, Referrer-Policy y X-Frame-Options,
ausentes en la versi贸n PHP.
"""
from __future__ import annotations
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from ..config import obtener_config
# CSP estricta: s贸lo mismo origen. El frontend inlinea lo m铆nimo; ajustar si se
# externalizan scripts. 'unsafe-inline' se evita salvo para estilos si fuese necesario.
# worker-src incluye blob: porque PDF.js crea su worker desde un blob URL
# (URL.createObjectURL + new Worker); sin ello el parseo de PDF en cliente falla.
_CSP = (
"default-src 'self'; "
"img-src 'self' data: blob:; "
"script-src 'self'; "
"worker-src 'self' blob:; "
"child-src 'self' blob:; "
"style-src 'self' 'unsafe-inline'; "
"font-src 'self'; "
"connect-src 'self'; "
"frame-ancestors 'none'; "
"base-uri 'self'"
)
class CabecerasSeguridad(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
resp = await call_next(request)
cfg = obtener_config()
resp.headers.setdefault("Content-Security-Policy", _CSP)
resp.headers.setdefault("X-Content-Type-Options", "nosniff")
resp.headers.setdefault("Referrer-Policy", "strict-origin-when-cross-origin")
resp.headers.setdefault("X-Frame-Options", "DENY")
resp.headers.setdefault("Permissions-Policy", "geolocation=(), microphone=(), camera=(self)")
if cfg.entorno == "prod":
resp.headers.setdefault(
"Strict-Transport-Security", "max-age=31536000; includeSubDomains"
)
return resp