Content Security Policy en je CMP: CSP en toestemming laten samenwerken
Twee beveiligingslagen die het eens moeten zijn
Een Content Security Policy (CSP) vertelt de browser van welke bronnen een pagina scripts, stijlen, afbeeldingen, frames en netwerkverzoeken mag laden. Een consent management platform doet het tegenovergestelde: het bepaalt welke van die scripts mogen draaien op basis van de keuze van de gebruiker. Worden beide los van elkaar geconfigureerd, dan botsen ze. De CMP laadt niet omdat CSP hem blokkeert, of de CSP wordt zo ver versoepeld om de CMP en advertentietags te accommoderen dat hij niets meer beschermt.
Deze gids legt uit welke CSP-directives een CMP doorgaans nodig heeft, hoe nonces samenwerken met scriptinjectie en hoe je wijzigingen uitrolt zonder je banner te breken.
Wat een CMP meestal van CSP nodig heeft
De exacte vereisten hangen af van de vendor, dus begin altijd bij de eigen documentatie van je CMP. Controleer in het algemeen deze directives:
script-src— het domein waarvan het CMP-script laadt, plus eventuele extra scriptbestanden die het laadt.connect-src— endpoints die de CMP aanroept metfetchof XHR, zoals configuratie, vendorlijst, geolocatie en toestemmingslogging.style-src— het domein van de stylesheet, en mogelijk inline stijlen als de CMP een<style>-element injecteert of style-attributen instelt.img-src— logo's, iconen en eventuele trackingvrije pixels die de CMP gebruikt.font-src— als de banner eigen lettertypen laadt.frame-src— als de CMP iframes gebruikt, bijvoorbeeld voor het delen van toestemming tussen domeinen.
Onthoud op TCF-sites dat de CMP op de pagina ook een locatorframe genaamd __tcfapiLocator aanmaakt. Dat frame wordt zonder externe bron gemaakt, dus heeft normaal geen frame-src-vermelding nodig, maar het is verstandig dat in tests te bevestigen.
Het probleem met de inline stub
De meeste CMP's vragen je een klein inline script — de stub — in de pagina-head te plaatsen, zodat andere scripts aanroepen in de wachtrij kunnen zetten voordat de hoofd-CMP laadt. Een strikte CSP blokkeert inline scripts tenzij je ze uitdrukkelijk toestaat. Je hebt drie opties:
- Een nonce. Genereer per respons een willekeurige waarde, voeg die als
'nonce-...'toe aan de CSP-header en aan de<script>-tag van de stub. Dit is de meest flexibele optie voor dynamische pagina's. - Een hash. Voeg de SHA-256-hash van de exacte inhoud van de stub toe aan
script-src. Dit werkt goed voor statische sites, maar elke wijziging aan de stub, zelfs aan witruimte, breekt het. - De stub zelf hosten als extern bestand op je eigen domein, gedekt door
'self'. Hierbij moet je ervoor zorgen dat hij vroeg genoeg laadt.
Vermijd 'unsafe-inline' als sluiproute. Het staat elk inline script toe, waardoor veel van de bescherming van CSP verdwijnt.
Nonces en door de CMP geïnjecteerde scripts
Een CMP die scripts blokkeert tot toestemming werkt vaak door ze achter te houden en later te injecteren. Onder een op nonce gebaseerde CSP moeten ook die geïnjecteerde scripts zijn toegestaan. Er zijn twee gangbare benaderingen:
- Nonce-propagatie. De CMP leest de nonce uit zijn eigen scripttag en voegt hem toe aan elk script dat hij injecteert. Controleer of je CMP dit ondersteunt.
'strict-dynamic'. Dit sleutelwoord vertelt ondersteunende browsers scripts te vertrouwen die door een al vertrouwd script worden geladen. Het vereenvoudigt het leven met tag managers en CMP's die scripts injecteren, maar het betekent ook dat elk script dat je vertrouwt andere kan laden, dus vertrouw zorgvuldig.
Tag managers roepen dezelfde vraag op. Als je advertentie- en analysetags via een tag manager laadt, volg dan zijn documentatie over nonces en zorg dat CMP en tag manager een consistente aanpak gebruiken.
Advertentietags maken CSP moeilijker
Programmatic adverteren laadt scripts en frames van veel domeinen, en de lijst verandert wanneer demandpartners veranderen. Uitgevers kiezen meestal een van twee wegen:
- Een strikte CSP op pagina's zonder programmatic advertenties, en een ruimer beleid waar advertenties draaien.
- Een beleid dat zich richt op directives die beheersbaar blijven, zoals
frame-ancestors,object-src 'none'enbase-uri, terwijl de scriptbronnen ruimer blijven.
Hoe dan ook moet de CMP expliciet op de toelatingslijst staan, niet alleen gedekt door een wildcard, zodat een CSP-wijziging de banner nooit ongemerkt uitschakelt.
Uitrollen met de report-only-modus
Gebruik eerst de header Content-Security-Policy-Report-Only. De browser meldt overtredingen zonder iets te blokkeren, zodat je ziet wat een nieuw beleid zou breken.
- Zet het beleid in report-only-modus uit met een rapportage-endpoint.
- Laad pagina's in schone browserprofielen en doorloop de volledige toestemmingsflow: eerste bezoek, alles accepteren, alles weigeren, aangepaste keuze, intrekking.
- Bekijk de overtredingsrapporten en de browserconsole op geblokkeerde CMP-bronnen.
- Pas het beleid aan en schakel dan over op handhaving.
- Laat rapportage ingeschakeld na de handhaving om regressies op te vangen.
Symptomen van een CSP-conflict
- De banner verschijnt nooit, en de console toont een CSP-overtreding voor het CMP-script.
- De banner verschijnt maar zonder opmaak, omdat
style-srczijn CSS blokkeert. - Keuzes worden niet opgeslagen, omdat
connect-srcde aanroep voor toestemmingslogging blokkeert. - Advertenties of analytics laden na toestemming nooit, omdat geïnjecteerde scripts geen nonce hebben.
__tcfapiis niet gedefinieerd, omdat de inline stub werd geblokkeerd.
Belangrijkste punten
- Controleer
script-src,connect-src,style-src,img-srcenframe-srcvoor je CMP. - Sta de inline stub toe met een nonce of hash, niet met
'unsafe-inline'. - Zorg dat scripts die na toestemming worden geïnjecteerd een nonce krijgen of door
'strict-dynamic'gedekt zijn. - Test elk toestemmingspad in report-only-modus voordat je handhaaft.
- Houd de CMP expliciet op de toelatingslijst zodat CSP-wijzigingen hem niet kunnen uitschakelen.