Sécurité

Content Security Policy (CSP) : Guide Pratique

Implémenter une Content Security Policy efficace : directives, exemples concrets, erreurs courantes et debug DevTools. Guide pratique PME.

25 mars 20264 min de lectureWarDek Team

La Content Security Policy est l'un des mécanismes de sécurité les plus puissants disponibles dans les navigateurs modernes, et l'un des plus mal compris. Une CSP bien configurée rend les attaques XSS (Cross-Site Scripting) quasi impossibles, même si votre code contient des vulnérabilités. Une CSP mal configurée bloque vos propres scripts et brise votre site sans rien protéger.

Ce guide vous amène de zéro à une CSP fonctionnelle et efficace.

Pourquoi la CSP Existe

Le web a été conçu avec un modèle de confiance implicite : tout script chargé sur votre page a les mêmes droits que votre propre code. Cette conception, acceptable dans les années 1990, est devenue un problème majeur.

Sans CSP, si un attaquant injecte du JavaScript dans votre page (via une faille XSS, une librairie compromise, ou un commentaire utilisateur non filtré), ce script peut :

La CSP résout ce problème en indiquant au navigateur exactement quelles sources sont autorisées pour chaque type de ressource. Tout le reste est bloqué.

Syntaxe des Directives

Une CSP est un header HTTP avec une valeur textuelle structurée :

Content-Security-Policy: directive1 source1 source2; directive2 source3;

Directives essentielles

| Directive | Contrôle | |-----------|----------| | default-src | Fallback pour toutes les ressources non spécifiées | | script-src | Scripts JavaScript | | style-src | Feuilles de style CSS | | img-src | Images | | connect-src | Requêtes XHR, fetch, WebSockets | | font-src | Polices de caractères | | frame-src | Iframes | | object-src | Plugins (Flash, etc.) | | form-action | Destinations des soumissions de formulaires |

Sources disponibles

| Source | Signification | |--------|---------------| | 'self' | Le même domaine que la page | | 'none' | Rien n'est autorisé | | https: | Toutes les URLs HTTPS | | https://cdn.example.com | Ce domaine spécifique | | *.example.com | Tous les sous-domaines | | 'unsafe-inline' | Styles/scripts inline (déconseillé) | | 'unsafe-eval' | Fonctions d'évaluation dynamique (déconseillé) | | 'nonce-{value}' | Scripts avec l'attribut nonce correspondant | | 'strict-dynamic' | Fait confiance aux scripts approuvés pour charger d'autres scripts |

Exemples Progressifs

CSP basique (point de départ)

Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; object-src 'none';

Cette politique autorise uniquement les ressources de votre propre domaine. Elle est sécurisée mais bloquera la plupart des sites qui utilisent des CDN, Google Fonts ou Analytics.

CSP pour un site classique avec CDN

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://cdn.jsdelivr.net https://www.google-analytics.com;
  style-src 'self' https://fonts.googleapis.com;
  font-src 'self' https://fonts.gstatic.com;
  img-src 'self' data: https://www.google-analytics.com;
  connect-src 'self' https://www.google-analytics.com;
  object-src 'none';
  frame-ancestors 'none';

CSP stricte avec nonce (recommandée)

La technique du nonce permet d'autoriser des scripts inline spécifiques sans 'unsafe-inline'. Le serveur génère un identifiant aléatoire unique à chaque requête :

<!-- Le serveur génère un nonce unique à chaque requête -->
<script nonce="r4nd0m-v4lu3-ch4ng3-each-r3qu3st">
  // Ce script est autorisé car il possède le bon nonce
  initializeApp();
</script>
Content-Security-Policy: script-src 'nonce-r4nd0m-v4lu3-ch4ng3-each-r3qu3st' 'strict-dynamic'; object-src 'none'; base-uri 'none';

Implémentation en Node.js/Express :

import crypto from 'crypto'

app.use((req, res, next) => {
  // Générer un nonce cryptographiquement sécurisé à chaque requête
  const nonce = crypto.randomBytes(16).toString('base64')
  res.locals.nonce = nonce

  res.setHeader(
    'Content-Security-Policy',
    `script-src 'nonce-${nonce}' 'strict-dynamic'; object-src 'none'; base-uri 'none';`
  )
  next()
})

Implémentation en Next.js (middleware) :

// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import crypto from 'crypto'

export function middleware(request: NextRequest) {
  const nonce = Buffer.from(crypto.randomUUID()).toString('base64')

  const cspHeader = `
    default-src 'self';
    script-src 'nonce-${nonce}' 'strict-dynamic';
    style-src 'self' 'nonce-${nonce}';
    img-src 'self' blob: data:;
    font-src 'self';
    object-src 'none';
    base-uri 'self';
    form-action 'self';
    frame-ancestors 'none';
    upgrade-insecure-requests;
  `.replace(/\s{2,}/g, ' ').trim()

  const requestHeaders = new Headers(request.headers)
  requestHeaders.set('x-nonce', nonce)

  const response = NextResponse.next({ request: { headers: requestHeaders } })
  response.headers.set('Content-Security-Policy', cspHeader)

  return response
}

Erreurs Courantes

1. Utiliser 'unsafe-inline' pour les scripts

# MAUVAIS — annule toute protection XSS pour les scripts
script-src 'self' 'unsafe-inline';

# BON — utiliser des nonces ou des hashes
script-src 'self' 'nonce-{generated-nonce}';

'unsafe-inline' pour script-src annule pratiquement toute protection de la CSP. Si vous en avez besoin à court terme, utilisez Content-Security-Policy-Report-Only d'abord pour mesurer l'impact réel avant de durcir.

Note : 'unsafe-inline' est acceptable pour style-src car les injections CSS sont moins critiques que les injections JavaScript.

2. Oublier object-src

Si vous n'incluez pas object-src 'none', les éléments <object>, <embed> et <applet> héritent de default-src. Ces éléments sont des vecteurs d'attaque historiques qui ne sont presque jamais utilisés légitimement dans les applications modernes.

3. Wildcard trop large

# MAUVAIS — autorise n'importe quel script HTTPS
script-src https:;

# BON — lister explicitement les domaines autorisés
script-src 'self' https://cdn.votre-librairie.com;

4. Oublier les sous-domaines

Si votre CDN est sur static.exemple.com, vous devez l'ajouter explicitement à la liste des sources. 'self' ne couvre que le domaine exact, pas les sous-domaines.

Déployer en Mode Report-Only

Avant d'activer une CSP en production, utilisez le mode Report-Only pour collecter les violations sans rien bloquer :

Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; report-uri /api/csp-report
// Endpoint de collecte des rapports
app.post('/api/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
  console.error('CSP Violation:', req.body['csp-report'])
  // Envoyer à votre système de monitoring
  res.status(204).send()
})

Après quelques jours de collecte, analysez les violations pour ajuster votre politique avant de passer en mode bloquant.

Déboguer avec les DevTools

Chrome et Firefox affichent les violations CSP dans la console développeur (F12 > Console). Chaque violation indique :

Refused to load the script 'https://external-cdn.com/script.js' because it violates
the following Content Security Policy directive: "script-src 'self'".

Pour voir toutes les violations en temps réel pendant le développement, utilisez l'onglet "Network" filtré sur "blocked" ou l'extension "CSP Evaluator" de Google.

Vérifier Votre CSP avec WarDek

Écrire une CSP manuellement est une tâche complexe et sujette aux erreurs. Un header trop permissif ne protège rien ; un header trop restrictif casse votre site.

WarDek analyse automatiquement votre Content Security Policy et identifie :

Combinez notre vérification CSP avec notre guide complet des headers HTTP et notre scanner de protection XSS pour une couverture de sécurité complète.

Une CSP correctement configurée est l'une des mesures de sécurité les plus efficaces que vous puissiez mettre en place. Elle transforme une vulnérabilité XSS critique en un incident mineur contenu.

Analyser ma CSP gratuitement →

#csp#headers-securite#xss#javascript#securite-web

Scannez votre site gratuitement

WarDek détecte les vulnérabilités mentionnées dans cet article en quelques secondes.

Retour à Sécurité