39 lines
1.6 KiB
Python
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 : le frontend ne consomme l'API qu'au travers d'un proxy same-origin
|
|
# (proxy.conf.json en dev, ingress /api en prod, 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
|