Tous les articles

CSP pour Google Analytics et Tag Manager avec un nonce

CentralCSP Team ·

Dernière mise à jour:

Vous pouvez faire tourner Google Analytics 4 (GA4) et Google Tag Manager (GTM) sous une politique de sécurité du contenu (CSP) stricte sans l'affaiblir. Une CSP est un header de réponse HTTP qui indique au navigateur quels scripts et autres ressources une page peut charger et exécuter. La manière stricte d'autoriser GTM est un nonce sur le script d'amorçage GTM plus 'strict-dynamic', pas une liste de domaines Google dans votre directive de script.

L'erreur courante est d'allowlister googletagmanager.com et google-analytics.com dans script-src. Avec un nonce et 'strict-dynamic' en place, le navigateur ignore entièrement les allowlists de hosts pour les scripts, donc ces entrées ne font rien. Cet article montre l'approche qui marche vraiment, où les domaines Google ont réellement leur place, et un exemple de header complet que vous pouvez adapter.

Les nonces vous sont nouveaux ? Démarrer avec la Content Security Policy explique ce qu'est un nonce et comment en générer un, et comment construire une CSP solide couvre le workflow Report-Only d'abord que cet article suppose.

Le point clé, ne listez pas les hosts Google dans script-src

Quand script-src contient un nonce et 'strict-dynamic', le navigateur change sa façon de décider quels scripts peuvent s'exécuter. Il ignore les allowlists de hosts, 'self', et les sources de schéma pour les scripts. Il ne fait confiance qu'aux scripts qui portent le nonce correspondant, et aux scripts que ces scripts de confiance chargent.

Donc script-src https://www.googletagmanager.com https://www.google-analytics.com ne sert à rien d'utile ici. Le navigateur l'ignore silencieusement. Ajouter ces hosts ne fait pas fonctionner GTM et ne le fait pas échouer, c'est juste du poids mort qui masque le vrai mécanisme.

Le mécanisme qui fonctionne : mettez le nonce sur le script inline d'amorçage GTM. Ce script est de confiance parce qu'il porte le nonce. 'strict-dynamic' propage ensuite cette confiance à gtm.js, que GTM charge, puis à chaque tag que GTM injecte. Aucun domaine Google n'apparaît dans script-src. Cette propagation est aussi pourquoi GTM mérite d'être surveillé de près ; voyez comment les attaquants détournent Google Tag Manager.

L'amorçage GTM avec nonce

Le snippet GTM officiel de Google propage déjà le nonce à la requête gtm.js. Utilisez cette version, où le nonce du script inline est lu et copié sur la balise de script injectée :

gtm-bootstrap.html
<script nonce="{SERVER-GENERATED-NONCE}">(function(w,d,s,l,i){w[l]=w[l]||[];
w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});
var f=d.getElementsByTagName(s)[0], j=d.createElement(s),
dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;
var n=d.querySelector('[nonce]');
n&&j.setAttribute('nonce',n.nonce||n.getAttribute('nonce'));
f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXX');</script>

Deux détails rendent ceci compatible avec une CSP stricte :

  • L'attribut nonce="{SERVER-GENERATED-NONCE}" est ce qui permet à 'strict-dynamic' de faire confiance à ce script inline. Remplacez {SERVER-GENERATED-NONCE} par une valeur aléatoire base64 fraîche, par réponse, d'au moins 128 bits, la même que vous mettez dans le header CSP.
  • Les lignes qui lisent [nonce] et appellent j.setAttribute('nonce', ...) copient le nonce sur le script gtm.js injecté. Avec 'strict-dynamic', cette propagation n'est pas strictement requise pour les scripts que GTM charge, mais elle maintient le nonce en circulation pour les tags qui le vérifient, donc gardez-la.

Générez le nonce sur le serveur et ne le réutilisez jamais d'une réponse à l'autre. Les bases de la génération de nonce sont dans démarrer avec la Content Security Policy, donc cet article ne les répète pas. Sur Next.js, le nonce par requête vient du proxy et Next l'applique aux scripts qu'il rend ; voyez comment mettre en place un nonce CSP dans Next.js pour ce câblage, puis mettez le même nonce sur l'amorçage GTM ci-dessous. Pour le pas à pas spécifique à GTM, voyez GTM sous une CSP stricte dans Next.js.

Où les domaines Google ont leur place

'strict-dynamic' n'affecte que les scripts. GA4 et GTM font aussi des requêtes non-script : des requêtes de collecte, des images et une frame de debug. Celles-ci sont régies par d'autres directives, et elles ont toujours besoin que les hosts Google soient listés. C'est là que vont les domaines.

  • connect-src couvre les appels fetch et beacon que GA4 utilise pour envoyer des données. Utilisez des wildcards : https://*.googletagmanager.com https://*.google-analytics.com https://*.analytics.google.com https://www.google.com. Les wildcards comptent parce que GA4 envoie vers des endpoints de collecte régionaux comme region1.google-analytics.com. Un www.google-analytics.com nu bloque ce trafic régional.
  • img-src couvre les requêtes de pixels et d'images : https://*.google-analytics.com https://*.googletagmanager.com.
  • frame-src couvre la frame Preview et Debug de GTM : https://www.googletagmanager.com.

Le mode Preview de GTM et certains tags injectent des styles inline. Si vous voyez des violations de style-src, préférez un nonce de style ou des hashes précis plutôt que de rouvrir la politique avec 'unsafe-inline'. Les styles qu'un conteneur injecte dépendent des tags, donc testez-les contre votre propre conteneur en Report-Only.

Les tags de publicité et de conversion (Google Ads, DoubleClick, Floodlight) atteignent des hosts supplémentaires. N'ajoutez que les hosts dont les tags que votre conteneur déclenche réellement ont besoin, plutôt que d'allowlister tout l'ensemble d'avance. Report-Only vous dira exactement lesquels. Pour les listes de hosts dont d'autres produits Google ont besoin, voyez CSP pour les services Google.

Un exemple de header complet

Voici une politique stricte qui fait tourner GA4 et GTM. Notez que script-src ne porte que le nonce et 'strict-dynamic', sans aucun host Google :

Content-Security-Policy:
  script-src 'nonce-{SERVER-GENERATED-NONCE}' 'strict-dynamic';
  connect-src 'self' https://*.googletagmanager.com https://*.google-analytics.com https://*.analytics.google.com https://www.google.com;
  img-src 'self' https://*.google-analytics.com https://*.googletagmanager.com;
  frame-src https://www.googletagmanager.com;
  object-src 'none';
  base-uri 'none';
  report-uri https://<Endpoint-ID>.report.centralcsp.com;
  report-to csp-endpoint

Le {SERVER-GENERATED-NONCE} dans le header doit être exactement la même valeur que celle mise sur le script inline d'amorçage. object-src 'none' et base-uri 'none' sont là parce qu'une politique stricte en a besoin : base-uri 'none' en particulier empêche une balise <base> injectée de rediriger vos chargements de scripts avec nonce. report-uri et report-to envoient les reports de violation à votre collecteur pour que vous puissiez observer ce que la politique bloquerait.

Évitez unsafe-eval, préférez les Custom Templates

Une fonctionnalité de GTM casse le schéma strict. Les variables JavaScript personnalisées, et certains tags personnalisés hérités, exécutent du code via eval, ce qui exige 'unsafe-eval' dans script-src. Ce mot-clé réactive l'exécution de chaîne-vers-code sur toute votre page, ce qui est un véritable affaiblissement de la politique. Pour le tableau complet, voyez unsafe-eval et pourquoi les Custom JS de GTM en ont besoin.

Préférez les Custom Templates de GTM, qui sont sandboxés et n'ont pas besoin de 'unsafe-eval'. Si un tag de votre conteneur force 'unsafe-eval', traitez-le comme une cible de migration : reconstruisez-le en Custom Template pour pouvoir abandonner le mot-clé. Évitez d'ajouter 'unsafe-eval' juste pour faire fonctionner une seule variable.

unsafe-inline n'est pas l'approche

Vous verrez peut-être le conseil d'ajouter 'unsafe-inline' pour que le script d'amorçage GTM s'exécute. Ne le faites pas. 'unsafe-inline' réactive chaque script inline de la page, y compris ceux qu'un attaquant injecte, ce qui est exactement ce que la CSP est là pour bloquer. Le raisonnement complet est dans pourquoi ne jamais utiliser unsafe-inline en CSP.

Cela n'aide même pas ici. Dès qu'une directive contient un nonce ou 'strict-dynamic', le navigateur ignore 'unsafe-inline' dans cette même directive. C'est le nonce sur le script d'amorçage qui fait tourner GTM, donc 'unsafe-inline' est à la fois dangereux et inerte.

Testez en Report-Only avant d'imposer

Les conteneurs GTM changent au fur et à mesure que votre équipe ajoute des tags, et chaque tag peut atteindre un host Google que votre politique n'a pas autorisé. Déployez cette politique d'abord sur le header Content-Security-Policy-Report-Only. Le navigateur ne bloque rien et signale tout ce qu'il aurait bloqué, donc vous voyez le host exact dont un nouveau tag a besoin avant qu'il ne puisse casser l'analytics en production.

CentralCSP collecte ces reports de violation Report-Only, les regroupe par directive et origine, et affiche les scripts qui tournent sur chaque page, pour que vous voyiez précisément quel host Google un tag veut avant d'imposer. Vous pouvez démarrer un essai gratuit, pointer un header Report-Only dessus, et regarder les reports arriver depuis du trafic réel. Pour auditer une politique qu'un site en production envoie déjà, le scanner CSP la vérifie, et l'évaluateur CSP la note pour des faiblesses comme une allowlist de hosts égarée ou un object-src manquant.

Questions fréquentes

Dois-je ajouter googletagmanager.com à script-src ?

Non. Avec un nonce et 'strict-dynamic' dans script-src, le navigateur ignore les allowlists de hosts pour les scripts. Vous mettez le nonce sur le script d'amorçage GTM à la place, et 'strict-dynamic' fait confiance à gtm.js et aux tags que GTM charge.

Pourquoi GA4 est-il encore bloqué avec le bon script-src ?

Parce que GA4 envoie des données via connect-src, pas script-src. Ajoutez https://*.google-analytics.com et les hosts Google associés à connect-src. Utilisez le wildcard pour que les endpoints régionaux comme region1.google-analytics.com soient autorisés.

GTM exige-t-il unsafe-eval ?

Seulement si vous utilisez des variables JavaScript personnalisées ou certains tags personnalisés hérités. Reconstruisez-les en Custom Templates de GTM, qui sont sandboxés et tournent sans 'unsafe-eval', pour pouvoir garder le mot-clé hors de votre politique.

Puis-je vraiment faire tourner GTM avec une CSP stricte basée sur un nonce ?

Oui. Utilisez le snippet d'amorçage officiel de Google qui propage le nonce, mettez le nonce par réponse dessus et dans le header CSP, et ajoutez 'strict-dynamic'. C'est la configuration CSP stricte prise en charge pour GTM et GA4.

À retenir

Une CSP stricte et Google Analytics ne sont pas en conflit. Mettez un nonce frais par réponse sur le script d'amorçage GTM, ajoutez 'strict-dynamic' à script-src, et laissez la confiance se propager à gtm.js et aux tags qu'il charge. Gardez les hosts Google hors de script-src, où ils sont ignorés, et ajoutez-les à connect-src, img-src et frame-src, où les requêtes se produisent réellement. Évitez 'unsafe-eval' en préférant les Custom Templates, et testez le tout en Report-Only d'abord pour qu'un nouveau tag ne casse jamais l'analytics en production.

Pour aller plus loin : le guide CSP de MDN et les recommandations GTM et CSP de Google.