Een Content Security Policy (CSP) is een HTTP-responseheader die de browser vertelt uit welke bronnen een pagina scripts, styles, afbeeldingen en verbindingen mag laden. Alles wat niet op die allowlist staat, wordt geblokkeerd voordat het wordt uitgevoerd. Gestandaardiseerd door het W3C is CSP de belangrijkste browser-native verdediging tegen cross-site scripting (XSS) en script-injectie.
Kort samengevat: wat is CSP
- CSP werkt door een allowlist per resourcetype te declareren (script-src, img-src, connect-src, enz.). Alles wat niet op de lijst staat wordt door de browser geblokkeerd voordat het wordt uitgevoerd.
- Het wordt geleverd als een HTTP-responseheader of een
<meta>-tag in de<head>van de pagina, en met Report-Only-modus test je een policy zonder de site te breken. - CSP is een basis, geen volledige verdediging. Het bepaalt waar een script vandaan laadt, niet wat het script doet zodra het draait, dus een gecompromitteerd maar toegestaan domein wordt nog steeds uitgevoerd.
- CSP alleen is niet genoeg voor PCI DSS 4.0.1-compliance. Eis §6.4.3 vereist script-inventaris en autorisatie; §11.6.1 vereist tamperdetectie. Beide gaan verder dan wat CSP kan afdwingen, vooral tegen een gecompromitteerd toegestaan domein zoals bij het Polyfill.io-incident van 2024.
Waar staat CSP voor?
CSP staat voor Content Security Policy. Het is een HTTP-response header (ook in te stellen via een <meta>-tag in de <head> van de pagina) die de browser vertelt welke bronnen van scripts, stijlen, afbeeldingen en verbindingen een pagina mag laden, en alles wat niet op die allowlist staat blokkeert voordat het wordt uitgevoerd.
Content Security Policy (CSP) begrijpen
Content Security Policy (CSP) is een browserbeveiligingsfunctie die is geïmplementeerd om bepaalde soorten browsergebaseerde aanvallen te beperken, zoals cross-site scripting. De CSP is gestandaardiseerd door het World Wide Web Consortium (W3C) in de CSP Level 3-specificatie. Het stelt een website in staat om een set regels te sturen (via HTTP-response headers of <meta>-tags in de HTML <head>) die de browser instrueren welke contentbronnen zijn toegestaan.
Deze regels, directives genoemd, specificeren goedgekeurde origins voor scripts, afbeeldingen, stijlen, iframes en meer. Het primaire doel van CSP is volledige controle te hebben over waar scripts vandaan worden geladen, samen met controle over welke scripts een pagina mag uitvoeren, om zo te proberen geïnjecteerde of ongeautoriseerde scripts tegen te houden.
Bijvoorbeeld, een CSP-directive kan bepalen dat scripts alleen mogen laden vanaf het eigen domein van de site (met ‘self’), of vanaf specifieke vertrouwde domeinen. De browser blokkeert dan elk scriptbestand of inline script dat niet van een toegestane bron komt. Dit beschermt tegen XSS-aanvallen, waarbij een aanvaller probeert kwaadaardige <script>-tags of code in een site te injecteren. Tegenwoordig ondersteunen de meeste browsers ook de ‘Report-only’-modus van CSP. Hiermee kunnen ontwikkelaars een policy veilig testen. Indien ingeschakeld, worden policy-overtredingen gelogd naar een rapportage-endpoint in plaats van direct te worden geblokkeerd. Dit is een best practice bij het voor de eerste keer implementeren van CSP.
Hoe Content Security Policy (CSP) werkt
Een Content Security Policy wordt aan een browser geleverd via de HTTP-response header genaamd Content-Security-Policy, of via een meta tag in de HTML <head>. De policy bestaat uit directives gescheiden door puntkomma’s, en elke directive controleert een specifiek type bron.
- ‘script-src’ self: staat alleen scripts toe van dezelfde origin. Elk
<script>van een ander domein (of inline scriptcode) wordt geblokkeerd door de browser. - ‘connect-src’ self https://api.domain.com : staat AJAX/XHR/fetch-aanroepen alleen toe naar dezelfde site of naar het vertrouwde domein api.domain.com. Dit voorkomt dat kwaadaardige code data exfiltreert naar onbekende servers vanaf de site.
- img-src ‘self’ data: kan worden gebruikt om alleen afbeeldingen van dezelfde site te laden en externe afbeeldingen te blokkeren - wat kan worden gebruikt om datalekken via afbeeldingsverzoeken te voorkomen.
CSP-nonces en CSP-hashes
CSP ondersteunt ook geavanceerde mechanismen zoals nonces (‘nonce-abc123’) en hashes (‘sha256-xyz…’). Deze stellen inline scripts in staat veilig uit te voeren door cryptografisch hun integriteit te bewijzen. In plaats van alle inline code te verbieden, kunnen ontwikkelaars selectief specifieke scripts autoriseren. Dit verbetert de flexibiliteit zonder in te leveren op beveiliging.
Er zijn talloze andere directives die kunnen worden gebruikt voor andere datatypen zoals media, fonts, iframes, etc., maar de kerngedachte van een Content Security Policy is het whitelisten van vertrouwde bronnen. Wanneer de browser een pagina laadt en bepaalt welke content geladen moet worden, raadpleegt deze eerst de CSP en dwingt deze regels af bij elke laadactie. Elk script of elke bron die deze policy overtreedt, wordt niet geladen. Voor een gedetailleerde implementatiegids, bekijk de OWASP CSP Cheat Sheet.
Veelvoorkomende CSP-directives en hun doel
| Directive | Doel | Typisch gebruik |
|---|---|---|
default-src | Stelt een basisbeleid in voor alle bronnen wanneer geen andere regel van toepassing is. | Begin strikt: default-src 'none'; |
script-src | Bepaalt welke JavaScript-bronnen zijn toegestaan. | Whitelist 'self', CDN’s, of gebruik nonce/hash-gebaseerde scripts. |
style-src | Beperkt waar CSS vandaan mag laden. | Gebruik 'self'; vermijd 'unsafe-inline' waar mogelijk. |
img-src | Definieert vertrouwde afbeeldingsbronnen. | Voorkom datalekken via externe afbeeldingsaanroepen. |
connect-src | Beperkt AJAX-, fetch- en WebSocket-bestemmingen. | Blokkeer data-exfiltratie naar onbekende domeinen. |
frame-ancestors | Specificeert welke sites je pagina’s mogen embedden in iframes. | Voorkom clickjacking: frame-ancestors 'none'; |
report-uri / report-to | Definieert waar CSP-overtredingsrapporten naartoe worden gestuurd. | Log en analyseer CSP-schendingen voor policy-afstemming. |
CSP-headervoorbeelden die je vandaag kunt kopiëren
Hieronder staan copy-paste startpunten voor de meest voorkomende CSP-implementaties. Test eerst in de modus Content-Security-Policy-Report-Only, kijk in de rapporten naar legitieme scripts die geblokkeerd zouden worden, en schakel dan over naar afdwingmodus.
1. Strikt startbeleid (standaard weigeren + allowlist)
Content-Security-Policy:
default-src 'none';
script-src 'self' https://cdn.example.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://api.example.com;
font-src 'self' https://fonts.gstatic.com;
form-action 'self';
frame-ancestors 'none';
base-uri 'self';
upgrade-insecure-requests;
report-uri /csp-report;
report-to csp-endpoint;
2. Op nonce gebaseerde inline scripts
Content-Security-Policy: script-src 'nonce-r@nd0mNonceHere' 'strict-dynamic';
<script nonce="r@nd0mNonceHere">
// this script executes; anything without the matching nonce does not
</script>
3. Op hash gebaseerde inline scripts
Content-Security-Policy: script-src 'sha256-B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8=';
Genereer de hash met: echo -n "your inline script contents" | openssl dgst -sha256 -binary | openssl base64
4. Report-Only-modus (geen blokkering, alleen monitoring)
Content-Security-Policy-Report-Only:
default-src 'self';
script-src 'self' https://cdn.example.com;
report-to csp-endpoint;
5. Report-To endpoint (moderne rapportage)
Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://example.com/csp-report"}]}
Content-Security-Policy: default-src 'self'; report-to csp-endpoint;
6. Voorbeeld van een overtredingspayload
{
"csp-report": {
"document-uri": "https://example.com/checkout",
"referrer": "",
"violated-directive": "script-src 'self'",
"effective-directive": "script-src",
"original-policy": "default-src 'self'; report-uri /csp-report",
"disposition": "enforce",
"blocked-uri": "https://malicious.example/skimmer.js",
"line-number": 42,
"source-file": "https://example.com/checkout",
"status-code": 200,
"script-sample": ""
}
}
7. Express.js-middleware
app.use((req, res, next) => {
res.setHeader(
"Content-Security-Policy",
"default-src 'self'; script-src 'self' https://cdn.example.com; report-uri /csp-report",
);
next();
});
app.post("/csp-report", express.json({ type: "application/csp-report" }), (req, res) => {
console.log("CSP violation", req.body);
res.sendStatus(204);
});
8. Cloudflare Worker
export default {
async fetch(request, env) {
const response = await fetch(request);
const headers = new Headers(response.headers);
headers.set(
"Content-Security-Policy",
"default-src 'self'; script-src 'self' https://cdn.example.com; report-to csp-endpoint",
);
return new Response(response.body, { status: response.status, headers });
},
};
Uitgebreide directive-referentie
Naast de zeven directives hierboven definieert CSP er nog meer die opduiken bij productie-hardeningswerk:
| Directive | Doel |
|---|---|
strict-dynamic | Vertrouwt scripts die geladen worden door een reeds vertrouwd (nonce of hash) script. Vereenvoudigt het laden via CDN’s. |
upgrade-insecure-requests | Upgradet http:-subresources automatisch naar https:. Elimineert mixed-content-waarschuwingen. |
require-trusted-types-for | Handhaaft Trusted Types op gevaarlijke DOM-sinks (bv. innerHTML, Function()). |
sandbox | Past iframe-achtige sandboxing toe op de hele pagina. Krachtig, vereist zorgvuldig testen. |
form-action | Beperkt waar <form>-submissies naartoe mogen POSTen. Blokkeert phishing-redirects. |
base-uri | Vergrendelt het <base>-element op specifieke origins. Voorkomt <base>-injectie-aanvallen. |
manifest-src | Bepaalt van waaruit een web-app-manifest geladen mag worden. |
media-src | Beperkt <audio>- en <video>-bronnen. |
object-src | Beperkt <object>, <embed>, <applet>. Zet op 'none' op moderne sites. |
worker-src | Beperkt Web Worker- en Service Worker-bronnen. |
Pas op met 'unsafe-inline' en 'unsafe-eval'. Beide worden nog ondersteund maar schakelen het grootste deel van de CSP-bescherming tegen XSS uit. 'strict-dynamic' met nonces is het moderne vervangingspad.
Zie het op je eigen site
CSP is één laag van client-side-verdediging, en een kritieke laag voor PCI DSS 4.0.1-compliance. Maar CSP alleen stopte de Polyfill.io-aanval in 2024 niet, omdat het gecompromitteerde domein op de allowlist stond. cside legt script-inventaris en payload-analyse bovenop CSP om dat gat te dichten. Gratis niveau beschikbaar.
Hoe voorkomt CSP browsergebaseerde aanvallen?
Beperking van XSS-aanvallen
Bij een cross-site scripting aanval, oftewel een XSS-aanval, vindt een aanvaller over het algemeen een manier om kwaadaardige JavaScript-code in je pagina te injecteren en uit te voeren (bijvoorbeeld via een niet-gesaniteerde input). Standaard blokkeert een CSP alle inline scripts op de pagina, tenzij een policy-directive dit expliciet toestaat. Dit betekent dat als een aanvaller iets als <script>evilCode()</script> in een pagina injecteert, dit niet wordt uitgevoerd (tenzij ‘unsafe-inline’ is toegestaan in de CSP!)
Blokkeren van ongeautoriseerde third-party scripts
Veel sites hebben third-party scripts voor zaken als analytics, gebruikerstracking en advertenties. Met een CSP kunnen site-eigenaren beperken welke externe sites daadwerkelijk scripts mogen leveren. Bijvoorbeeld, als je alleen content wilt serveren vanaf analytics._example_.com en niets anders van example.com, kan de script-src-directive van de CSP van je site expliciet alleen dat toestaan.
Voorkomen van data-exfiltratie
Zoals eerder genoemd, kan een CSP acties op een pagina beperken die gebruikt kunnen worden om te voorkomen dat kwaadaardige JavaScript-code data terugstuurt naar een door een aanvaller gecontroleerd domein. Het gebruik van de connect-src-directive blokkeert netwerkverzoeken naar ongeautoriseerde servers, en kan worden gecombineerd met de form-action-directive om te verzekeren dat data alleen naar jouw domein wordt gestuurd.
Afdwingen van veilige browserpraktijken
Een CSP heeft directives om veilig gedrag af te dwingen dat de beveiliging van je site in het algemeen verbetert.
- Een voorbeeld hiervan is
upgrade-insecure-requests, die de browser dwingt alle bronnen via HTTPS te laden, waardoor gemengde en onveilige content wordt voorkomen. - Een andere directive is
frame-ancestors, die clickjacking-aanvallen kan voorkomen door niet toe te staan dat je pagina wordt geëmbed in een door een aanvaller gecontroleerd frame.
Gecombineerd resulteren deze policies in een robuuste client-side basis voor moderne webapplicaties.
Beveiligingsbeperkingen van CSP
Het gebruik van een Content Security Policy biedt sterke bescherming, maar het is geen allesomvattende oplossing voor je site, zoals belicht in ons artikel “Waarom Content Security Policy niet werkt”.
Policies kunnen na verloop van tijd afdrijven, en allowlists inspecteren geen codegedrag. Elke grote browser implementeert CSP op zijn eigen, licht verschillende manier. Chrome, Firefox, Safari en Edge ondersteunen allemaal CSP Level 3; rapportagegedrag en overtredingsformaten kunnen variëren. Om je policy te valideren en te onderhouden, test deze regelmatig met browser developer tools en geautomatiseerde scanners zoals Mozilla Observatory of SecurityHeaders.io.
Het combineren van een CSP met een actieve client-side beveiligingslaag zoals cside om real-time inspectie en blokkering toe te voegen aan third-party scripts op je site geeft je een uitstekende verdedigingslaag, met gemoedsrust voor je klanten. Vanuit governance-perspectief verbetert het documenteren van CSP-updates en het monitoren van overtredingsrapporten de controleerbaarheid en langetermijncompliance met frameworks zoals ISO 27001 en OWASP ASVS.
Werkt CSP voor PCI DSS 6.4.3-compliance?
Onder PCI DSS 4.0.1 Vereiste 6.4.3 moeten merchants bewijzen dat elk client-side script is geautoriseerd en de integriteit van scripts aantonen. CSP en SRI brengen je een deel van de weg. CSP beperkt welke domeinen scripts mogen laden, en SRI controleert of de code van een bestand niet is veranderd. Maar samen zijn ze een statische oplossing voor een dynamisch probleem. Dynamische scripts worden bijgewerkt en hashes breken daardoor. Handmatig onderhoud van CSP-lijsten is vrijwel onmogelijk.
De meeste moderne sites gebruiken dynamische third-party scripts, dus deze controles verslechteren snel. Deze aanpak zal doorgaans tekortschieten in het vereiste bewijs voor PCI 6.4.3.
Een Content Security Policy (CSP)-voorbeeld: toen een strikte CSP de productie brak
De dag dat de checkout stopte met werken: hoe een strikte CSP productie brak
Het begon allemaal met een gewone nieuwe deployment. Niets bijzonders, gewoon een nieuwe Content Security Policy om een webwinkel veiliger te maken en aanvallers te verhinderen stiekem kwaadaardige code binnen te smokkelen.
De schone default-src ‘none’ was ingesteld zonder testen. En dus, op het moment dat de CSP werd geactiveerd, blokkeerde de website diensten die het eigenlijk nodig had. Analytics stopte met werken en, erger nog, het betalingssysteem werd geblokkeerd. Klanten konden hun bestellingen niet afronden en het checkout-systeem brak. Ontwikkelaars gingen aan het werk en schakelden de header om naar Content-Security-Policy-Report-Only en verzamelden overtredingslogs. Van daaruit bouwden ze een allowlist (script-src ‘self’ https://pay.examplecase.com) met alle diensten die de webshop nodig had om goed te functioneren.
Na het verfijnen van de CSP kon het team zonder problemen deployen. Dit soort onoplettendheid is een veelvoorkomend probleem wanneer teams CSP intern beheren. Het vergeten om een nieuw marketingscript te whitelisten, technische misconfiguraties, of problemen met dynamische scripts die hashingprotocollen breken, maken CSP een nachtmerrie om op schaal te beheren.
Genereer en onderhoud je CSP automatisch met cside
Een Content Security Policy met de hand schrijven en onderhouden is waar de meeste teams vastlopen: elke nieuwe marketingtag, dynamisch script van derden of gewijzigd endpoint van een leverancier kan de policy breken of de allowlist stilletjes verbreden. cside neemt dat handmatige werk weg.
cside kijkt naar de scripts die je site daadwerkelijk laadt en genereert een kant-en-klare CSP-header voor je op basis van echte browsersessies, en houdt die actueel naarmate die scripts veranderen, zodat je niet handmatig achter allowlist-updates hoeft aan te lopen. Je krijgt een Content Security Policy die weerspiegelt wat je site echt uitvoert, plus de scriptinventarisatie en manipulatiedetectie die een CSP alleen niet kan bieden voor de eisen §6.4.3 en §11.6.1 van PCI DSS 4.0.1.
Er valt niets te bouwen. Meld je aan voor het gratis abonnement, voeg je domein toe en cside begint je CSP te genereren op basis van live verkeer. Ontdek de CSP-beheeroplossing of zie hoe cside de gaten dicht die een CSP openlaat.