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-srcautorise'self'sansunsafe-inlineen 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 ciblehttps://<host Matomo>/matomo.js, couverte par l'origine ajoutée àscript-src.Pourquoi
style-srcautorise'unsafe-inline'en prod ? Solid pose des styles inline (<span style="...">) sur des nœuds existants (cf.SpriteTile.tsx). La directivestyle-srcvise les<style>statiques et lesstyle="..."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/noncesstyle-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
- Plugin Vite :
apps/web/vite-plugin-csp.ts - Plugin SEO recette (doit passer par
transformIndexHtml) :apps/web/vite-plugin-recipe-seo.ts - Issue : #104
- Spec : MDN — Content Security Policy (CSP)
- Spec : MDN — Permissions Policy