Files
Dorian ac05da7001
Airflow / Construction de l'image (push) Successful in 1m17s
Backend / Tests exigeant une base (push) Failing after 4m50s
Backend / Analyse statique de sécurité (push) Successful in 7s
Airflow / Lint et intégrité des DAGs (push) Successful in 9m34s
Backend / Audit des dépendances (push) Successful in 9m36s
Backend / Lint, typage et tests (push) Successful in 10m7s
SonarQube / test-ml (push) Failing after 6m8s
SonarQube / build-front (push) Successful in 10m15s
SonarQube / build-back (push) Successful in 10m47s
SonarQube / test-front (push) Failing after 5m13s
SonarQube / test-back (push) Failing after 5m22s
SonarQube / SonarQube (push) Skipped
docs(backend): corrige la justification du CORP same-origin (no-cors, pas d'ingress)
2026-09-23 10:30:06 +02:00

39 lines
1.6 KiB
Python

# Pourquoi : `SecurityHeadersMiddleware` ne pose ni HSTS ni CSP, et c'est délibéré.
# L'application ignore si TLS termine devant elle, donc elle ne peut pas décider d'un HSTS ;
# et une CSP sur une API JSON ne protège presque rien, celle qui compte protège la page
# Angular. Les deux appartiennent au terminateur TLS.
# Contrainte : `/docs` charge Swagger depuis un CDN, une CSP stricte ici casserait la
# documentation sans rien sécuriser.
from collections.abc import Awaitable, Callable
from typing import Final
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response
EN_TETES: Final[dict[str, str]] = {
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
"Referrer-Policy": "no-referrer",
# same-origin : aucun client ne charge l'API en no-cors depuis une autre origine
# (proxy.conf.json en dev, reverse proxy nginx ensuite, cf. docs/architecture/20-backend.md).
"Cross-Origin-Resource-Policy": "same-origin",
}
PREFIXE_AUTHENTIFICATION: Final = "/auth"
class SecurityHeadersMiddleware(BaseHTTPMiddleware):
async def dispatch(
self, request: Request, call_next: Callable[[Request], Awaitable[Response]]
) -> Response:
response = await call_next(request)
for nom, valeur in EN_TETES.items():
response.headers.setdefault(nom, valeur)
# Une réponse d'authentification ne doit jamais être conservée par un intermédiaire.
if PREFIXE_AUTHENTIFICATION in request.url.path:
response.headers["Cache-Control"] = "no-store"
return response