Aller au contenu principal
Petanque Life

Security Headers & CORS

F16.11 8 fonctionnalités Planifié

En bref

HTTP security headers and CORS policy enforced through SecurityHeadersMiddleware on every response: per-environment, per-app CORS allowlist with wildcard rejection in production, Content-Security-Policy with per-request nonces, HSTS preload, X-Frame-Options DENY, strict Referrer-Policy, locked-down Permissions-Policy disabling sensitive APIs by default and a Subresource Integrity manifest covering all CDN-served assets.

Comment ça fonctionne

SecurityHeadersMiddleware (provided by craft-easy and tuned with Petanque Life defaults) is mounted ahead of every other handler so every response — including errors — carries the right headers. CORS is configured per environment and per app: production ships an explicit origin allowlist for the admin app, the player app, the federation CMS, the marketing site and petanque.life itself; development adds localhost; wildcard origins are rejected at config-load through a model_validator so an accidental commit cannot loosen production. Allowed methods, headers, exposed headers, credentials and preflight max-age are each configurable.

Content-Security-Policy is enforced with per-request nonces. SecurityHeadersMiddleware generates a fresh secrets.token_urlsafe(16) per request, injects it into the CSP script-src and style-src directives via a {nonce} placeholder, and exposes it on request.state.csp_nonce so SSR templates can stamp the matching nonce on inline blocks. Frame-ancestors is 'none', base-uri is 'self', form-action is 'self' and upgrade-insecure-requests is on, so a hijacked third-party script cannot inject untrusted content or downgrade transport.

HSTS ships with max-age=31536000; includeSubDomains; preload on every HTTPS response, omitted on plain HTTP, and respects X-Forwarded-Proto behind the Container Apps ingress so the proxy terminates TLS but the origin still sees the right scheme. X-Frame-Options is DENY by default — admin and API will not load inside an iframe — configurable through SECURITY_HEADERS_X_FRAME_OPTIONS for the rare embed case. Referrer-Policy defaults to strict-origin (stricter than craft-easy's strict-origin-when-cross-origin), so outbound links leak the origin only, never path or query.

Permissions-Policy is locked down per app type: camera, microphone, geolocation, payment, USB, magnetometer, gyroscope, accelerometer, ambient-light-sensor, autoplay all disabled by default; fullscreen, encrypted-media and picture-in-picture allowed only same-origin so the apps can still play media and go fullscreen for scoreboards. Subresource Integrity is managed through an SriAsset model and a public manifest at GET /sri/manifest with sha256/384/512 hash computation; admin CRUD lets ops add or rotate CDN assets and re-verify, ensuring a compromised CDN cannot serve malicious payloads under a trusted URL. Security.txt (RFC 9116) is on the roadmap to advertise the disclosure channel.

Capacités clés

  • Per-environment, per-app CORS allowlist with wildcard rejection in production
  • Content-Security-Policy with per-request nonces injected into script-src and style-src
  • HSTS preload (max-age=31536000; includeSubDomains; preload) with X-Forwarded-Proto support
  • X-Frame-Options DENY default to prevent clickjacking
  • Referrer-Policy strict-origin (stricter than framework default)
  • Permissions-Policy disabling camera, microphone, geolocation, payment, USB and motion sensors
  • Subresource Integrity manifest with sha256/384/512 hash computation and admin CRUD
  • Security.txt (RFC 9116) for responsible disclosure (planned)

En pratique

A penetration tester runs an external scan against api.petanque.life. The report shows A+ on Mozilla Observatory: HSTS preload locked in, CSP with nonces and frame-ancestors 'none', X-Frame-Options DENY, Referrer-Policy strict-origin, Permissions-Policy disabling everything sensitive. He tries to embed the admin app in an attacker page — the browser refuses to render the frame.

He tries an XSS via inline script — the CSP rejects it because it lacks the per-request nonce. He inspects a CDN-loaded asset and pulls the SRI hash from /sri/manifest; rotating the asset on the CDN without updating the manifest would break the integrity check and the browser would refuse to execute. The findings come back clean — no headers required for the next quarterly review.

Fonctionnalités de ce sous-système

8
ID Status Fonctionnalités
F16.11.01 Livré CORS-policy per miljö och app — per-environment CORS origin allowlist med production-origins för admin/app/www/petanque.life + localhost i dev, wildcard-reject i production via model_validator, credentials/methods/headers/expose/max-age konfigurerat. Implemented (PL-F1611a). ✅ PL-F1611a
F16.11.02 Livré Content-Security-Policy med nonces — per-request secrets.token_urlsafe(16) nonce injicerat i CSP script-src och style-src via {nonce}-placeholder, frame-ancestors 'none', base-uri 'self', form-action 'self', upgrade-insecure-requests. Nonce tillgängligt via request.state.csp_nonce. Implemented (PL-F1611a). ✅ PL-F1611a
F16.11.03 Livré HSTS preload-list inclusion — Strict-Transport-Security: max-age=31536000; includeSubDomains; preload på alla HTTPS-svar, HSTS-header utelämnad på plain HTTP, proxy-stöd via X-Forwarded-Proto. Implemented (PL-F1611a). ✅ PL-F1611a
F16.11.04 Livré X-Frame-Options DENY för admin — X-Frame-Options: DENY som default (konfigurerbar via SECURITY_HEADERS_X_FRAME_OPTIONS), förhindrar clickjacking på admin- och API-endpoints. Implemented (PL-F1611a). ✅ PL-F1611a
F16.11.05 Livré Referrer-Policy strict-origin — konfigurerbar via SECURITY_HEADERS_REFERRER_POLICY, Petanque Life default strict-origin (striktare än craft-easy default strict-origin-when-cross-origin). Skickar bara origin, aldrig path/query. Implemented (PL-F1611b). ✅ PL-F1611b
F16.11.06 Livré Permissions-Policy per app-typ — konfigurerbar via SECURITY_HEADERS_PERMISSIONS_POLICY, inaktiverar camera, microphone, geolocation, payment, usb, magnetometer, gyroscope, accelerometer, ambient-light-sensor, autoplay. Tillåter fullscreen, encrypted-media, picture-in-picture för same-origin. Implemented (PL-F1611b). ✅ PL-F1611b
F16.11.07 Livré Subresource Integrity (SRI) för CDN-assets — SriAsset-modell, hash-beräkning (sha256/384/512), publikt manifest (GET /sri/manifest), admin CRUD, re-verifiering. Implemented (PL-F1611b). ✅ PL-F1611b
F16.11.08 Livré Security.txt (RFC 9116) — /.well-known/security.txt på alla publika domäner (petanque.life, *.web.petanque.life, api/admin/app/sys/design.petanque.life). Shared TS-generator i packages/shared/src/security/security-txt.ts, FastAPI-route i api/src/petanque_api/routes/well_known.py, build-time-genererad fil för Expo SWA via tools/security/generate-security-txt.mjs. PGP-nyckel + disclosure-policy (en/sv/fr/es) + hall-of-fame på petanque.life. Veckovis CI-kontroll av Expires (tools/security/verify_security_txt.py). Implemented (PL-T287). ✅ PL-T287