diff --git a/backend/.env.example b/backend/.env.example index 951e0255..cf663a1b 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -15,3 +15,7 @@ SESSION_SECRET_KEY=change-me-to-a-random-secret FEATURES__DATASET_REVIEW=true FEATURES__HEALTH_MONITOR=true APP_TIMEZONE=Europe/Moscow + +# HSTS — включать только при настроенном HTTPS (nginx/ingress) +# Включение без сертификатов сломает доступ к сайту! +# FORCE_HTTPS=true diff --git a/backend/src/app.py b/backend/src/app.py index aecfaee5..60228db6 100755 --- a/backend/src/app.py +++ b/backend/src/app.py @@ -191,11 +191,23 @@ app.add_middleware( ) # HSTS — Strict-Transport-Security header (see [SEC:L-2]) +# Only active when FORCE_HTTPS=true (enterprise deployments with proper certs). +# In dev or behind HTTP-only proxies, HSTS is disabled to avoid lockout. class HSTSMiddleware(BaseHTTPMiddleware): - """Add Strict-Transport-Security header to every response.""" + """Add Strict-Transport-Security header when FORCE_HTTPS=true. + + Enterprise note: In production, set HSTS at the nginx/ingress level + (add_header Strict-Transport-Security ...). This middleware is a + fallback for environments where nginx is not configured to do so. + """ + def __init__(self, app): + super().__init__(app) + self._enabled = os.getenv("FORCE_HTTPS", "").strip().lower() in {"1", "true", "yes"} + async def dispatch(self, request: Request, call_next): response = await call_next(request) - response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains" + if self._enabled: + response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains" return response app.add_middleware(HSTSMiddleware)