# Construire une CSP solide, étape par étape (/fr/blog/how-to-build-a-strong-csp)





Une politique de sécurité du contenu (CSP) est un en-tête de réponse HTTP qui indique au navigateur quels scripts, styles et autres ressources une page est autorisée à charger et exécuter. C'est la défense la plus efficace dont vous disposiez dans le navigateur contre le cross-site scripting (XSS) et le vol de données par script. Le plus difficile n'est pas la syntaxe, c'est d'écrire une politique qui verrouille la page sans la casser.

La méthode fiable tient en deux temps : mesurer d'abord, appliquer ensuite. Vous partez d'une politique stricte en mode report-only, vous observez ce que le trafic réel aurait enfreint, vous construisez la vraie politique à partir de ces preuves, vous la validez, puis vous activez l'application et continuez à surveiller. Cet article déroule ce flux de bout en bout, avec l'en-tête de départ exact et la configuration par framework.

Vous débutez avec la CSP ? Commencez par [démarrer avec la politique de sécurité du contenu](/fr/blog/get-started-with-csp) pour les bases, ce qu'est la CSP, votre première politique et votre premier nonce, puis revenez ici pour la construction complète.

## Le flux en une minute [#le-flux-en-une-minute]

Voici le processus complet, avant d'entrer dans le détail :

1. **Démarrez en Report-Only.** Déployez une politique délibérément stricte sur l'en-tête `Content-Security-Policy-Report-Only`. Le navigateur ne bloque rien et signale tout ce qu'il aurait bloqué.
2. **Posez l'en-tête sur votre serveur ou votre framework.** Utilisez le mécanisme prévu par votre stack, pour que l'en-tête parte sur chaque réponse.
3. **Collectez les rapports de violation depuis le trafic réel.** Pointez la politique vers un endpoint de reporting et laissez les vrais utilisateurs générer les données.
4. **Construisez la vraie politique à partir de ce que vous voyez.** Chaque rapport vous indique une origine ou une ressource inline dont votre page a réellement besoin. Ajoutez le minimum pour l'autoriser.
5. **Validez.** Notez la politique, confirmez qu'elle n'a pas de contournement évident et vérifiez qu'elle est vraiment stricte.
6. **Appliquez.** Faites passer la politique validée de `Content-Security-Policy-Report-Only` à `Content-Security-Policy`.
7. **Continuez à surveiller.** Le code que vous ajoutez et les tiers changent ce que la page charge. Gardez le flux de rapports ouvert pour repérer les ruptures et les altérations.

Les mêmes sept étapes sous forme de flux :

```mermaid
flowchart LR
  A["1. Report-Only<br/>strict policy"] --> B["2. Set the<br/>header"]
  B --> C["3. Collect<br/>real reports"]
  C --> D["4. Build the<br/>real policy"]
  D --> E["5. Validate"]
  E --> F["6. Enforce"]
  F --> G["7. Keep<br/>monitoring"]
```

Le principe derrière chaque étape : n'appliquez jamais une politique que vous n'avez pas mesurée contre le trafic réel. Report-Only est ce qui rend cela sûr.

## Étape 1 : commencez par une politique Report-Only stricte [#étape-1--commencez-par-une-politique-report-only-stricte]

Commencez par une politique volontairement trop stricte. En mode report-only, le navigateur n'applique rien : une politique trop serrée ne vous coûte donc rien, sinon un flux de rapports qui vous disent exactement ce dont la page dépend. Ce sont précisément les données que vous cherchez.

Envoyez ceci sur l'en-tête `Content-Security-Policy-Report-Only` comme point de départ :

```http
Content-Security-Policy-Report-Only:
  default-src 'self';
  script-src 'self' 'report-sample';
  style-src 'self';
  img-src 'self';
  font-src 'self';
  object-src 'none';
  base-uri 'none';
  form-action 'none';
  frame-ancestors 'none';
  frame-src 'self';
  connect-src 'none';
  upgrade-insecure-requests;
  report-uri https://<Endpoint-ID>.report.centralcsp.com;
  report-to csp-endpoint
```

Ce que fait chaque partie :

* `default-src 'self'` sert de repli à toute directive de fetch que vous ne nommez pas : tout ce qui n'est pas listé se limite donc à la même origine.
* `script-src 'self' 'report-sample'` autorise les scripts de même origine. `'report-sample'` indique au navigateur d'inclure un court échantillon, les 40 premiers caractères, de tout code inline fautif dans le rapport, ce qui rend les violations bien plus faciles à identifier. Il est accepté dans `script-src`, `style-src` et leurs variantes `-elem` et `-attr`.
* `object-src 'none'` bloque les plugins. `base-uri 'none'` bloque l'injection d'une balise `<base>`, qui pourrait sinon détourner les URL relatives et déjouer une politique fondée sur les nonces. `form-action 'none'` interdit l'envoi des formulaires, quelle que soit la destination. `frame-ancestors 'none'` est une protection anti-clickjacking et le remplacement moderne de `X-Frame-Options` ; si vous ne pouvez pas définir de header CSP du tout, voyez [la protection contre le clickjacking quand vous ne pouvez pas définir de header CSP](/fr/blog/frame-ancestors-without-csp-header). `frame-src 'self'` limite ce que la page peut encadrer.
* `upgrade-insecure-requests` met à niveau les requêtes de sous-ressources non sécurisées vers HTTPS avant qu'elles n'atteignent le réseau, sans repli HTTP. Elle ne prend aucune valeur. Elle ne met pas à niveau les navigations de premier niveau cross-origin et elle ne remplace pas HSTS ; voyez [HSTS vs upgrade-insecure-requests](/fr/blog/hsts-vs-upgrade-insecure-requests) pour savoir en quoi les deux diffèrent.
* `report-uri` et `report-to` envoient les rapports de violation à votre collecteur. Plus de détails sur ces deux directives à l'étape 3.

### Deux pièges que cette politique est conçue pour révéler [#deux-pièges-que-cette-politique-est-conçue-pour-révéler]

Ce point de départ n'est volontairement pas applicable tel quel. Deux directives vont générer beaucoup de rapports, et c'est justement le but.

`connect-src 'none'` bloque chaque requête `fetch`, `XMLHttpRequest`, WebSocket et `EventSource`. Cela cassera presque tout site réel dès que vous l'appliquerez. Partir de `'none'` oblige chaque appel réseau de votre page à se signaler dans un rapport, et vous les voyez donc tous. Presque toutes les applications finissent par avoir besoin au moins de `connect-src 'self'` plus leurs origines d'API et d'analytics.

`style-src 'self'` fera remonter les styles inline, que des frameworks comme Angular et les bibliothèques CSS-in-JS injectent en permanence. Vous réglez ça avec un nonce de style, plutôt qu'en ouvrant la politique avec [`'unsafe-inline'`](/fr/blog/unsafe-inline-csp).

> La politique de départ est un instrument de mesure, pas votre politique finale. Attendez-vous à beaucoup de rapports le premier jour. C'est le flux qui fait son travail.

## Étape 2 : posez l'en-tête CSP sur votre serveur ou votre framework [#étape-2--posez-len-tête-csp-sur-votre-serveur-ou-votre-framework]

Une politique ne sert à rien si l'en-tête n'atteint pas le navigateur sur chaque réponse, pages d'erreur comprises. Utilisez le mécanisme prévu par votre stack. Chaque exemple ci-dessous pose un en-tête CSP en mode application ou report-only ; remplacez le nom de l'en-tête et les directives par la politique de départ de l'étape 1. Pour plus de stacks et des snippets complets, voyez [comment définir le header CSP dans chaque framework](/fr/blog/set-csp-header-every-framework).

### Nginx [#nginx]

Avec le [`ngx_http_headers_module`](https://nginx.org/en/docs/http/ngx_http_headers_module.html), utilisez `add_header` avec `always` pour que l'en-tête soit envoyé aussi sur les réponses d'erreur :

```nginx
add_header Content-Security-Policy "default-src 'self'" always;
```

### Apache [#apache]

Avec [`mod_headers`](https://httpd.apache.org/docs/current/mod/mod_headers.html) :

```apache
Header always set Content-Security-Policy "default-src 'self'"
```

### Traefik [#traefik]

Avec le [middleware headers](https://doc.traefik.io/traefik/middlewares/http/headers/), posez un en-tête de réponse personnalisé :

```yaml
traefik.http.middlewares.csp.headers.customresponseheaders.Content-Security-Policy=default-src 'self'
```

### Express (Helmet) [#express-helmet]

[Helmet](http://helmet.js.org/) pose l'en-tête pour vous et prend directement en charge le mode report-only. Les valeurs de directive acceptent des fonctions, et c'est comme ça qu'on fournit un nonce neuf à chaque requête :

```javascript
app.use(
  helmet({
    contentSecurityPolicy: {
      directives: { "script-src": ["'self'"] },
      reportOnly: true,
    },
  })
);
```

### Django (django-csp) [#django-django-csp]

Ajoutez `csp.middleware.CSPMiddleware`, puis définissez la politique dans les settings. Utilisez [`CONTENT_SECURITY_POLICY_REPORT_ONLY`](https://django-csp.readthedocs.io/en/latest/configuration.html) pour la phase report-only et `CONTENT_SECURITY_POLICY` une fois que vous appliquez :

```python title="settings.py"
CONTENT_SECURITY_POLICY_REPORT_ONLY = {
    "DIRECTIVES": {
        "default-src": ["'self'"],
        "script-src": ["'self'", "'report-sample'"],
    },
}
```

### Next.js (App Router) [#nextjs-app-router]

Pour une politique stricte fondée sur les nonces avec un rendu dynamique, générez un nonce par requête dans `proxy.ts` et émettez `script-src 'self' 'nonce-...' 'strict-dynamic'`. Le [mot-clé `'strict-dynamic'`](/fr/blog/strict-dynamic-csp) laisse les scripts de confiance en charger d'autres, sans allowlist d'hôtes. Pour une politique statique, renvoyez l'en-tête depuis [`async headers()`](https://nextjs.org/docs/app/guides/content-security-policy) dans `next.config.js`. C'est la route proxy qui vous permet de servir un vrai nonce ; pour la configuration complète, voyez [comment configurer un nonce CSP avec Next.js](/fr/blog/csp-nonce-nextjs).

### Nuxt (nuxt-security) [#nuxt-nuxt-security]

Configurez [`security.headers.contentSecurityPolicy`](https://nuxt-security.vercel.app/headers/csp) dans `nuxt.config.ts`. Sa valeur par défaut est déjà une politique à nonce strict avec `'strict-dynamic'` : vous resserrez à partir d'une bonne base au lieu de partir de zéro.

### Laravel (spatie/laravel-csp) [#laravel-spatielaravel-csp]

Enregistrez le middleware `Spatie\Csp\AddCspHeaders::class`, puis définissez votre politique via des presets dans [`config/csp.php`](https://github.com/spatie/laravel-csp). Le package fournit des presets report-only pour la phase de collecte.

### Angular [#angular]

Angular ne pose pas l'en-tête lui-même : c'est à votre serveur de le faire, avec l'un des mécanismes ci-dessus. Ce qu'Angular apporte, c'est un nonce par requête, fourni via le [token `CSP_NONCE` ou l'attribut `ngCspNonce`](https://angular.dev/best-practices/security). Associez-le à un `script-src 'self' 'nonce-...'` posé côté serveur.

## Étape 3 : collectez les rapports de violation depuis le trafic réel [#étape-3--collectez-les-rapports-de-violation-depuis-le-trafic-réel]

La politique de départ pointe déjà vers un endpoint de reporting. De vrais utilisateurs sur de vraies pages génèrent maintenant les preuves à partir desquelles vous construisez la politique. Les tests synthétiques passent à côté de la longue traîne : scripts tiers, analytics régionaux, pages de cas limites. Collectez donc sur le trafic réel.

### Envoyez les rapports à votre collecteur [#envoyez-les-rapports-à-votre-collecteur]

Utilisez la directive `report-to` avec l'en-tête de réponse `Reporting-Endpoints`, qui nomme votre collecteur. `report-uri` est son prédécesseur déprécié ; vous pouvez encore l'envoyer sur la même politique, en pointant vers le même collecteur. Les différences sont dans [report-uri vs report-to](/fr/blog/report-uri-vs-report-to).

```http
Reporting-Endpoints: csp-endpoint="https://<Endpoint-ID>.report.centralcsp.com"
```

```http
Content-Security-Policy-Report-Only:
  default-src 'self';
  script-src 'self' 'report-sample';
  ...
  report-uri https://<Endpoint-ID>.report.centralcsp.com;
  report-to csp-endpoint
```

### Lisez les rapports, ne vous y noyez pas [#lisez-les-rapports-ne-vous-y-noyez-pas]

Le JSON brut des rapports CSP arrive une violation à la fois et devient vite des milliers d'enregistrements quasi identiques. Tout le travail consiste à les regrouper : quelles origines distinctes et quelles ressources inline la page utilise-t-elle vraiment, et lesquelles ressemblent à du bruit ou à une tentative d'injection ?

C'est exactement ce pour quoi [CentralCSP](/platform/csp-builder) est conçu. Il ingère vos rapports de violation Report-Only, les regroupe par directive et par origine, et affiche les scripts qui s'exécutent sur chaque page grâce au reporting de hash CSP : vous voyez donc quoi autoriser, avant d'appliquer. Vous pouvez [démarrer un essai gratuit](/register), y pointer un en-tête Report-Only et regarder les rapports arriver du trafic réel. Si vous préférez d'abord auditer une politique existante, le [scanner CSP](/tools/csp-scanner) vérifie ce qu'un site en ligne envoie déjà.

<img alt="Les violations issues du trafic réel, regroupées par directive et origine bloquée" src="__img0" width="1365" height="691" />

## Étape 4 : construisez la vraie politique à partir de ce que vous voyez [#étape-4--construisez-la-vraie-politique-à-partir-de-ce-que-vous-voyez]

Transformez maintenant les rapports regroupés en politique. Travaillez directive par directive et tranchez, pour chaque rapport, entre trois issues : la ressource est légitime et vous l'autorisez avec la source la plus étroite possible, vous pouvez la supprimer ou l'héberger vous-même, ou elle est suspecte et vous enquêtez.

Quelques règles empiriques à garder en tête :

* Ajoutez des origines spécifiques, pas des jokers. Si les rapports montrent des appels `connect-src` vers votre API et un hôte d'analytics, listez ces deux origines, pas `https:`.
* Hébergez vous-même tout ce que vous pouvez raisonnablement héberger. Moins d'origines tierces, c'est une politique plus courte et une surface d'exposition plus réduite sur la chaîne d'approvisionnement.
* Pour les scripts et styles inline dont vous avez vraiment besoin, utilisez un nonce ou un [hash](/tools/csp-hash), jamais `'unsafe-inline'`. Le mot-clé `'report-sample'` de la politique de départ et l'inventaire de scripts vous indiquent quels blocs inline sont les vôtres.

### Rendez script-src vraiment solide [#rendez-script-src-vraiment-solide]

C'est l'étape qui sépare une politique moyenne d'une politique solide, et elle s'applique à la politique appliquée, pas au point de départ du premier jour.

Les listes d'autorisation d'hôtes dans `script-src` sont faibles. Le moindre endpoint de redirection ouverte ou JSONP sur un hôte autorisé peut servir de contournement. L'approche solide consiste à abandonner les listes d'hôtes pour les scripts et à ne faire confiance qu'aux nonces ou aux hashes, avec `'strict-dynamic'` :

```http
Content-Security-Policy: script-src 'self' 'nonce-r4nd0m' 'strict-dynamic'; object-src 'none'; base-uri 'none'
```

`'strict-dynamic'` indique au navigateur d'ignorer les listes d'hôtes, `'self'` et `'unsafe-inline'` pour les scripts, et de ne faire confiance qu'aux scripts marqués par un nonce ou un hash, ainsi qu'à ceux que ces scripts chargent. C'est la façon moderne d'autoriser des scripts sans listes d'hôtes fragiles.

Quoi que vous fassiez, ne comptez pas sur `'unsafe-inline'` pour faire taire les erreurs de script. Cela réautorise exactement l'exécution inline que la CSP est là pour bloquer. Le raisonnement complet est dans [pourquoi vous ne devriez jamais utiliser 'unsafe-inline' dans une CSP](/fr/blog/unsafe-inline-csp), et il en va de même pour son cousin : voyez [unsafe-eval et comment le supprimer](/fr/blog/unsafe-eval-csp).

Le cas le plus fréquent, ce sont les tag managers tiers. Pour faire tourner [GA4](https://support.google.com/analytics/answer/10089681) et Google Tag Manager sous un nonce avec `'strict-dynamic'`, sans lister les hôtes Google dans `script-src`, voyez [CSP avec Google Analytics et Tag Manager](/fr/blog/csp-google-analytics-tag-manager).

Pour la liste complète des directives, valeurs et mots-clés au moment d'assembler la politique, voyez la [référence des politiques CSP](/fr/docs/web-security/policies/content-security-policy).

## Étape 5 : validez la politique [#étape-5--validez-la-politique]

Avant d'appliquer, vérifiez que la politique est stricte et sans contournement évident. Une politique peut être syntaxiquement valide et ne servir à rien en pratique, par exemple un `script-src` qui retombe sur `'unsafe-inline'` ou qui autorise un hôte connu pour héberger des scripts arbitraires.

Passez-la dans l'[évaluateur CSP](/tools/csp-evaluator) : il note la nouvelle politique et signale les sources faibles, un `object-src` ou un `base-uri` manquant, un joker qui ruine tout l'intérêt. Corrigez ce qu'il remonte, puis revérifiez. C'est aussi le moment de vous assurer que vous n'avez pas élargi une directive sans le vouloir en courant après les rapports à l'étape 4.

## Étape 6 : appliquez [#étape-6--appliquez]

Quand le flux Report-Only s'est calmé, c'est-à-dire quand les seules violations restantes sont du bruit ou de vraies tentatives que vous avez tout intérêt à bloquer, promouvez la politique. Déplacez exactement les mêmes directives de l'en-tête `Content-Security-Policy-Report-Only` vers l'en-tête appliqué `Content-Security-Policy`.

Gardez aussi un `report-uri` et un `report-to` sur l'en-tête appliqué. Application et reporting ne s'excluent pas : une politique appliquée envoie quand même un rapport pour chaque ressource qu'elle bloque, et c'est ainsi que vous découvrirez qu'elle casse quelque chose qui vous avait échappé.

Une pratique courante et plus sûre consiste à laisser tourner les deux en-têtes en parallèle pendant un temps : la politique validée en application, et une candidate encore plus stricte en Report-Only, pour continuer à resserrer sans risque.

## Étape 7 : continuez à surveiller [#étape-7--continuez-à-surveiller]

Une CSP n'est pas un en-tête que l'on pose et que l'on oublie. Chaque nouvelle fonctionnalité, chaque montée de version d'une dépendance, chaque balise tierce peut amener une origine ou un bloc inline que votre politique n'autorise pas. Résultat : soit la page casse, soit, plus inquiétant, quelque chose a changé sur la page à votre insu.

Gardez le flux de rapports ouvert en production. Deux choses à surveiller :

* **Rupture :** une nouvelle ressource légitime que votre politique bloque. Le rapport vous dit quoi ajouter.
* **Altération :** des scripts ou des origines qui apparaissent sans que personne dans votre équipe les ait introduits. Sur une page de paiement, c'est le signal précoce de formjacking ou d'un skimmer de type Magecart.

La surveillance continue des rapports CSP, l'inventaire de scripts avec détection des CVE et les alertes sur les scripts nouveaux ou modifiés forment le cœur de la [suite CSP de CentralCSP](/platform/csp-builder). Sur les pages de paiement, cette surveillance continue des scripts est ce qui vous aide à répondre aux exigences côté client de PCI DSS v4 (6.4.3 et 11.6.1) ; voyez [CSP pour PCI DSS v4](/fr/blog/csp-pci-dss-v4) pour savoir comment politique et exigences se recoupent. La plateforme ne certifie pas la conformité : elle fournit la surveillance et des preuves exportables, et c'est votre QSA qui valide.

## Foire aux questions [#foire-aux-questions]

### Combien de temps faut-il faire tourner une CSP en Report-Only ? [#combien-de-temps-faut-il-faire-tourner-une-csp-en-report-only-]

Assez longtemps pour voir passer le trafic réel sur vos pages et vos tiers, souvent une à deux semaines. Arrêtez quand les seules violations restantes sont du bruit ou de vraies tentatives que vous avez tout intérêt à bloquer.

### Qu'est-ce qu'une bonne CSP de départ ? [#quest-ce-quune-bonne-csp-de-départ-]

Une politique Report-Only délibérément stricte : `default-src 'self'`, `object-src 'none'`, `base-uri 'none'`, `frame-ancestors 'none'`, et un `script-src` que vous resserrez vers les nonces. Utilisez l'en-tête de départ ci-dessus et laissez les rapports vous guider.

### Faut-il utiliser report-uri ou report-to ? [#faut-il-utiliser-report-uri-ou-report-to-]

Utilisez `report-to` avec l'en-tête `Reporting-Endpoints` ; c'est le mécanisme actuel. `report-uri` est le prédécesseur déprécié, que vous pouvez encore inclure sur la même politique si vous le souhaitez.

### Qu'est-ce qui rend script-src solide ? [#quest-ce-qui-rend-script-src-solide-]

Des nonces ou des hashes avec `'strict-dynamic'`, à la place des listes d'autorisation d'hôtes. Un endpoint de redirection ouverte ou JSONP sur un hôte autorisé ne peut alors plus servir de contournement par injection de script.

## Un rapide récapitulatif de l'ordre [#un-rapide-récapitulatif-de-lordre]

La force d'une CSP vient de l'ordre des étapes, pas d'une directive en particulier :

* Politique stricte en **Report-Only** d'abord, pour que la mesure ne coûte rien.
* **Collectez** sur le trafic réel, parce que les tests synthétiques passent à côté de la longue traîne.
* **Construisez** au plus juste, un rapport à la fois, avec des nonces et `'strict-dynamic'` pour les scripts.
* **Validez**, **appliquez**, puis **continuez à surveiller**, parce que la page ne cesse de changer.

Un mot sur la construction de la politique elle-même : un CSP Builder guidé, qui transforme vos rapports collectés en une politique prête à livrer, arrive bientôt, avec son propre guide pratique. En attendant, ce flux vous donne une politique solide, écrite à la main.

Pour aller plus loin : le [guide de MDN sur la CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP) et la [spécification W3C CSP Level 3](https://www.w3.org/TR/CSP3/).

## Articles liés [#articles-liés]

* [Bien démarrer avec le reporting CSP](/fr/blog/get-started-csp-reporting)
* [Comment utiliser le CSP Builder](/fr/blog/how-to-use-csp-builder)
