GTM vs. CSP

GTM vs. Content Security Policy — schéma konfliktu mezi trackingem a bezpečností

Vývojář nasadí Content Security Policy na firemní web — a tracking se v tichosti rozbije. Nebo naopak: analytik přidá tag do Google Tag Manager (GTM) a protlačí výjimku v CSP — měření funguje, ale web je děravý.

Google Tag Manager (GTM) je ze své podstaty script injector — vkládá do stránky cizí skripty. Content Security Policy (CSP) je script blocker — blokuje vše, co není na allowlistu. Když je nasadíte oba bez rozmyslu, buď přijdete o data, nebo o bezpečnost. Nejde o HTTP hlavičky. Jde o to, jestli máte data pro rozhodování — a jestli jsou v bezpečí.

V předchozím článku jsem ukázal, co všechno se může stát, když GTM nemá dohled — od nevinných chyb po zdokumentované útoky na e-shopy. Teď se podíváme na technické řešení: jak nastavit CSP tak, aby měření fungovalo a web zůstal chráněný.

Co je Content Security Policy

CSP je HTTP hlavička, kterou server říká prohlížeči: „Smíš spouštět skripty jen z těchto zdrojů.“ Nic víc, nic míň.

Content-Security-Policy: 
  script-src 'self' https://*.googletagmanager.com;
  connect-src 'self' https://*.google-analytics.com;
  img-src 'self' https://*.google-analytics.com;

Pro analytiku jsou relevantní hlavně tyto direktivy:

  • script-src — které skripty se smí spouštět (nejdůležitější)
  • connect-src — kam se smí posílat data (XHR, fetch, beacony)
  • img-src — odkud se smí načítat obrázky (a trackingové pixely)
  • frame-src — povolené iframy
  • style-src — styly (relevantní pro GTM preview mode)

Prohlížeč nerozlišuje mezi „hodným“ skriptem GA4 a škodlivým JavaScriptem. Pokud skript není na allowlistu, prohlížeč ho zablokuje. Tiše, bez varování pro uživatele — a často bez varování pro vás.

CSP chrání proti XSS útokům, data exfiltration a injektování škodlivého kódu. Dělá to dobře. Problém nastává ve chvíli, kdy do rovnice vstoupí tag manager.

Proč je GTM pro CSP noční můra

GTM funguje ve dvou úrovních — a obě jsou pro CSP problém.

První úroveň: kontejnerový snippet. Inline skript v stránky. Potřebuje povolení v script-src.

Druhá úroveň: tagy načtené kontejnerem. GTM si stáhne konfiguraci a dynamicky vloží do stránky další skripty — GA4, Meta Pixel, Hotjar, LinkedIn Insight Tag… Každý vendor potřebuje vlastní domény v CSP.

Každý nový tag v GTM = potenciální nová doména v CSP. Markeťák přidá tag za 2 minuty. Změna CSP hlavičky vyžaduje Jira ticket, code review, deploy a čekání.

Reálné důsledky špatné konfigurace:

  • Tichý data loss — tracking přestane fungovat, ale nikdo si toho nevšimne. Data gap se nedá zpětně doplnit.
  • Rozbité kampaně — konverzní pixely neodpálí, kampaně nelze vyhodnotit.
  • Nefunkční debug — GTM preview mode vyžaduje direktivy pro tagmanager.google.com, bez nich ladění nefunguje.

Za chvíli ukážu, jak to řešit. Nejdřív je ale potřeba pochopit, proč špatně nastavené CSP může být horší než žádné.

Když lék zabíjí: GTM jako bezpečnostní riziko

Bezpečnostní firma Raxis demonstrovala útok, ve kterém využila GTM kontejner k obejití WAF i CSP:

  1. Útočník najde XSS zranitelnost na webu
  2. Vloží odkaz na vlastní GTM kontejner se škodlivým Custom HTML tagem
  3. Payload žije na googletagmanager.com — WAF ho nevidí jako hrozbu, CSP ho povolí
  4. Kód se spustí v kontextu stránky — přístup ke cookies, session tokenům, formulářům

Google to uznal jako „honorable mention“ v Bug Bounty programu. Víc zdokumentovaných útoků přes GTM — včetně krádeží platebních údajů ze stovek e-shopů — v předchozím článku.

Tři podmínky musí platit současně: (1) XSS zranitelnost na webu, (2) CSP bez nonce pro GTM skripty, (3) unsafe-inline nebo jiná slabá direktiva v CSP. Splňujete všechny tři? Máte problém.

Věděli jste? — informační panel

Vědecká poznámka

Je to jako kobří efekt — britská koloniální správa v Indii vypsala odměnu za usmrcené kobry. Lidé začali kobry chovat. Když vláda odměnu zrušila, chovatelé kobry vypustili a populace vzrostla. Řešení zhoršilo problém. CSP nasazené „aby web byl bezpečný“, ale oslabené kvůli GTM na unsafe-inline, funguje stejně — vytváří falešný pocit bezpečí a otevírá dveře útočníkům.

Špatně nastavené CSP + GTM je horší než žádné CSP. Ne jen psychologicky — i technicky. CSP s unsafe-inline explicitně říká prohlížeči „spouštěj jakýkoli inline skript.“ Výsledek je stejný jako bez CSP, ale bezpečnostní audity vidí „CSP: ano“ a jdou dál. Tým přestane investovat do dalších ochran, protože „CSP máme.“ A útok typu Raxis ukazuje, jak přesně tuto falešnou jistotu zneužít.

Nonce + strict-dynamic: bezpečná cesta

Za mě ideální cesta — pokud jde realizovat.

Nonce je náhodný token (řetězec znaků), který server vygeneruje pro každý HTTP response:

  1. Server vygeneruje unikátní nonce (např. a8f5e2b1)
  2. Nonce se vloží do CSP hlavičky: script-src 'nonce-a8f5e2b1' 'strict-dynamic'
  3. Stejný nonce se přidá na GTM snippet:

    strict-dynamic je CSP3 standard (definovaný v MDN specifikaci). Říká: „důvěřuj skriptům, které načte již schválený skript." Nemusíte vypisovat vendorové domény — GTM kontejner je schválený přes nonce a vše, co načte, dědí důvěru.

    Google oficiální dokumentace strict-dynamic přímo neuvádí. Je to ale zavedený a fungující přístup, doporučovaný komunitou.

    Pozor na Custom HTML tagy

    GTM propaguje nonce automaticky na built-in tagy a šablony z Community Template Gallery. Na Custom HTML tagy nonce nepropaguje.

    Pokud máte Custom HTML tag, musíte:

    1. Ručně přidat proměnnou {{nonce}} do každého do každého script tagu v Custom HTML
    2. Řešit Chrome-specific workaround — Chrome maskuje atribut nonce, takže potřebujete data-nonce

    Není to nemožné, ale je to křehké a vyžaduje údržbu při každém novém Custom HTML tagu.

    Omezení nonce přístupu

    • Nefunguje s full-page cache (každý response musí mít unikátní nonce)
    • Vyžaduje server-side úpravu — ne vždy mají analytici přístup k serveru
    • Edge cases s některými SPA frameworky (Next.js — client-side komponenty nemohou generovat nonce server-side)

    Whitelist domén: pragmatická cesta

    Nonce vyžaduje dynamické generování tokenu v aplikačním kódu při každém requestu — úpravy v middleware nebo serverovém frameworku. Whitelist je jednodušší: nastavíte statické hodnoty v konfiguraci webserveru (nginx, Apache), CDN, nebo přes tag. Obojí vyžaduje konfiguraci na straně serveru (nebo tag), ale whitelist je statická konfigurace, ne úprava aplikačního kódu:

    Content-Security-Policy:
      script-src 'self' https://*.googletagmanager.com;
      connect-src 'self' https://*.google-analytics.com 
                         https://*.analytics.google.com;
      img-src 'self' https://*.google-analytics.com;

    Pro každý další vendor (Meta, LinkedIn, Hotjar…) přidáte jeho domény.

    Funguje to? Ano.

    Je to elegantní? Ne.

    • Vendoři mění domény bez varování → CSP se rozbije a zjistíte to z absence dat
    • Geo-specifické domény (google.fr vs google.com) závisí na lokaci uživatele
    • Každý nový tag v GTM = Jira ticket, deploy, čekání
    • Údržba je průběžná — „nastavit a zapomenout" nefunguje

    Pro weby s full-page cache nebo tam, kde není kapacita na úpravu aplikační vrstvy, to ale může být jediná realistická cesta.

    Šablony vs. Custom HTML: tady se láme chleba

    Custom HTML tagy a Custom JavaScript proměnné jsou z pohledu CSP problematické. Custom JS proměnné vyžadují unsafe-eval v script-src — bez něj vrací undefined (Google dokumentace). Custom HTML tagy mohou fungovat s nonce, ale vyžadují ruční konfiguraci a browser workaroundy.

    unsafe-eval je ještě riskantnější než unsafe-inline — povoluje eval(), new Function() a další dynamické spouštění kódu. Přesně to, proti čemu CSP chrání.

    GTM šablony (Community Template Gallery nebo vlastní) tento problém nemají. Běží v sandboxovaném prostředí GTM kontejneru, fungují jako metody uvnitř gtm.js a nepotřebují unsafe-eval ani unsafe-inline. GTM API (sendPixel, injectScript, setCookie…) je navrženo pro práci s CSP.

    Google explicitně doporučuje migraci z Custom HTML na šablony jako bezpečnou alternativu.

    Pokud máte striktní CSP a Custom HTML tagy, volíte ze tří možností:

    1. Přepsat na šablony — nejbezpečnější, ale práce navíc. Navíc ne vše je možné vůbec do šablon přepsat.
    2. Ručně nakonfigurovat nonce pro Custom HTML tagy + browser workaroundy — funguje, ale křehké
    3. Povolit unsafe-eval / unsafe-inline jako fallback — nejjednodušší, ale podkopává účel CSP

    Jakou cestu zvolit? Záleží na počtu Custom HTML tagů a na tom, jak striktní bezpečnostní požadavky máte. Čím víc tagů a čím vyšší compliance nároky, tím víc se vyplatí investice do migrace na šablony.

    Jak do toho zapadá server-side GTM?

    sGTM může CSP mírně zjednodušit, ale rozhodně ho nevyřeší.

    Co sGTM reálně zjednoduší: loading GTM kontejneru. Soubor gtm.js se servíruje z vaší subdomény přes sGTM — obsah je identický s tím, co stahujete z Google domény, ale v script-src potřebujete jen vaši doménu místo googletagmanager.com. S Google Tag Gateway vám pak může stačit direktiva self.

    Co sGTM neřeší: web kontejner GTM stejně ve většině implementací stahuje skripty z domén třetích stran a posílá na ně data. Některé platformy vyžatují posílat data frontend i ze serveru (např. Meta Pixel + Conversions API přes sGTM). A některé nástroje (MS Clarity, Hotjar) nemají server-side verzi vůbec. Jejich skripty musí zůstat na stránce — a tím pádem i v CSP.

    Report-Only a monitoring: než zapnete ostrý provoz

    Rada pro každého, kdo s CSP začíná: nezapínejte enforcement hned.

    Začněte s Content-Security-Policy-Report-Only. Funguje jako ostrá CSP, ale místo blokování jen sbírá hlášení o violacích. Nechte ji běžet minimálně 7 dní.

    Content-Security-Policy-Report-Only:
      script-src 'nonce-{token}' 'strict-dynamic';
      report-uri /csp-report-endpoint;

    Kam posílat violation reporty

    • Dedikovaný endpoint logující do BigQuery nebo logovacího nástroje
    • JavaScript listener na securitypolicyviolation event — violace vidíte přímo v prohlížeči
    • GTM Tag Assistant — umí zobrazit zdroje blokované CSP

    Preview mode — přehlédnutý problém

    GTM debug mode vyžaduje vlastní direktivy. Bez nich ladění nefunguje:

    script-src: https://googletagmanager.com https://tagmanager.google.com
    style-src:  https://tagmanager.google.com https://fonts.googleapis.com
    img-src:    https://ssl.gstatic.com https://www.gstatic.com
    font-src:   https://fonts.gstatic.com data:

    Zapomenout na tohle znamená, že vám debug mode buď nebude fungovat vůbec, nebo bude ladění nepříjemné.

    Proces je důležitější než konfigurace

    Sebelepší CSP je k ničemu, pokud změna trvá měsíc. Rychlé workflow pro CSP aktualizace (ideálně do 1 dne) je stejně důležité jako samotné nastavení. Analytik musí vědět o CSP, vývojář o tracking závislostech.

    Závěry

    1. CSP a GTM nejsou nepřátelé. Ale vyžadují koordinaci mezi security a analytics týmy.
    2. Praktický krok: otevřete browser console na svém webu. Hledejte nějaké CSP hlášení (červené errory). Pokud tam jsou, víte, kde začít. Pokud ne — podívejte se, jestli CSP vůbec máte.
    3. Pokud jste ještě nečetli první díl o bezpečnostních rizicích GTM, začněte tam — ukazuje, co se může stát, když GTM nemá dohled.