Files
ben-to/docs/security-headers.md
T
2026-07-21 16:50:58 +02:00

5.5 KiB

En-têtes de sécurité et Content-Security-Policy

L'application web est durcie par défaut via le plugin Vite apps/web/vite-plugin-csp.ts. Il injecte une Content-Security-Policy dans le HTML de chaque page (et un serve.json en build) et émet plusieurs en-têtes de sécurité complémentaires.

Vue d'ensemble

En-tête Valeur Émis par
Content-Security-Policy default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self'; connect-src 'self'; media-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; frame-src 'none' Plugin Vite (dev + build)
X-Content-Type-Options nosniff Plugin Vite
Referrer-Policy strict-origin-when-cross-origin Plugin Vite
X-Frame-Options DENY Plugin Vite
Permissions-Policy accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=() Plugin Vite

En build de production, le plugin écrit en plus dist/serve.json ; le serveur statique serve (cf. Dockerfile : CMD ["serve", "-s", "dist", "-l", "3000"]) applique les en-têtes déclarés.

En dev (Vite), le plugin injecte la CSP via <meta http-equiv="Content-Security-Policy"> et pose les en-têtes de réponse via un middleware Connect. La CSP dev assouplit script-src et style-src avec 'unsafe-inline' + 'unsafe-eval', et connect-src avec ws: / wss: pour ne pas casser HMR.

Adapter la CSP à Matomo

Le serveur Matomo est optionnel (cf. matomo-analytics.md). Quand VITE_MATOMO_URL est défini au build, son origine est ajoutée à script-src, connect-src et img-src :

# Build local avec Matomo
VITE_MATOMO_URL=https://analytics.example.com/ \
  npm run build:web

La CSP générée ressemblera à :

default-src 'self';
script-src 'self' 'https://analytics.example.com';
style-src 'self';
img-src 'self' 'https://analytics.example.com';
font-src 'self';
connect-src 'self' 'https://analytics.example.com';
media-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
frame-src 'none'

Sans VITE_MATOMO_URL, aucune origine externe n'est ajoutée : la surface d'attaque est minimale.

Pourquoi script-src autorise 'self' sans unsafe-inline en prod ? Vite produit un bundle compilé référencé via <script type="module" src="/assets/..."> (même origine). Aucun script inline n'est généré. Le plugin Matomo injecte un script dynamiquement (cf. apps/web/src/infra/matomo.ts) : l'URL cible https://<host Matomo>/matomo.js, couverte par l'origine ajoutée à script-src.

Pourquoi style-src autorise 'unsafe-inline' en prod ? Solid pose des styles inline (<span style="...">) sur des nœuds existants (cf. SpriteTile.tsx). La directive style-src vise les <style> statiques et les style="..." du HTML, donc l'autorisation est nécessaire. Si on veut durcir davantage à l'avenir : extraire les styles inline vers des classes CSS et passer à style-src 'self' strict, ou utiliser des hash/nonces style-src-attr 'sha256-…'.

Vérification manuelle

# 1) Build et smoke test local
npm run pipeline:recipes
npm run build:web
npx serve -s apps/web/dist -l 3000 &
sleep 1

# 2) Vérifier les en-têtes
curl -sI http://localhost:3000/ | grep -Ei 'content-security-policy|x-frame|x-content|referrer|permissions'

# 3) Vérifier la balise meta dans une page recette
curl -s http://localhost:3000/r/base%3Aonigiri/variant%3Aonigiri-kimchi-mozza \
  | grep -i 'http-equiv="Content-Security-Policy"'

# 4) Vérifier que dist/serve.json est correct
cat apps/web/dist/serve.json

Côté navigateur, onglet Network → sélectionner la requête du document → panneau Headers : la section « Response Headers » doit afficher les cinq en-têtes ci-dessus. Aucune erreur CSP ne doit apparaître dans la console pour les actions courantes (navigation, partage, fermeture du bandeau cookies).

Tests automatisés

  • apps/web/vite-plugin-csp.unit.test.ts — helpers purs (construction CSP, sérialisation, injection HTML, serve.json).
  • apps/web/vite-plugin-csp.int.test.ts — scénarios Gherkin @int-csp-* (@int-csp-html-injection, @int-csp-matomo-origin, @int-csp-serve-json).
  • tests/e2e/security-headers.e2e.ts — vérifie en dev (Playwright + Vite middleware) la présence de la CSP et des en-têtes sur / et /r/..., et la balise meta dans le HTML rendu.

Lancer toute la chaîne :

npm run check:quality
npm run test:ci

Hors scope (V1)

  • Subresource Integrity (SRI) sur les bundles Vite — l'absence de CDN rend l'attaque peu probable, mais à étudier dans un ticket dédié.
  • report-uri / report-to — pas de collecteur configuré sur la V1.
  • CSP par nonce / hash pour autoriser des styles Solid inline en prod — non requis tant que la directive style-src 'self' n'engendre pas de violation observable.

Références