Hoe deze controle werkt
Deze controle treedt alleen op als je site al een Content-Security-Policy-header verstuurt; zo niet, dan markeert de headerscanner dat. Scanaris leest het volledige beleid en zoekt de twee woorden die het uithollen: 'unsafe-inline', dat elk script of elke stijl die rechtstreeks in de HTML staat laat uitvoeren, en 'unsafe-eval', dat toestaat tekst in code om te zetten met eval en consorten. Komt een van beide voor, dan opent het een bevinding met lage ernst en de lijst van gevonden zwaktes. Laag, omdat een onvolmaakte CSP nog altijd beter is dan geen, maar de bescherming tegen scriptinjectie, de bestaansreden van de CSP, is bijna tenietgedaan.
Waarom het ertoe doet
Een strikte CSP zorgt ervoor dat de browser een <script> weigert uit te voeren, zelfs als een aanvaller er een in je pagina krijgt (via een niet-opgeschoonde reactie, een gespiegelde parameter of een gecompromitteerde bibliotheek). Met 'unsafe-inline' in script-src draait dat geïnjecteerde script gewoon: de header is er, het slot lijkt dicht, maar de deur staat open. Waar het woord staat, is bepalend: in script-src is het ernstig; in style-src is het een veel kleiner risico dat veel sites accepteren vanwege hun inline stijlen.
Hoe je het oplost
1. Nonces voor inline scripts
Een nonce is een willekeurige waarde, bij elk verzoek anders, die je in de header en in elke legitieme <script>-tag zet. De browser voert alleen scripts met die waarde uit; een geïnjecteerd script kent hem niet.
Content-Security-Policy: script-src 'nonce-R4nd0m' 'strict-dynamic'; object-src 'none'; base-uri 'self'
Next.js doet dit in de middleware, die de nonce genereert en doorgeeft aan de header en de componenten; Nuxt met een serverplugin die hem in elk antwoord injecteert (zo doet Scanaris het); Express met een middleware die hem in res.locals bewaart en in de templates afdrukt.
2. Hashes voor vaste scripts
Verandert een inline script nooit, bereken dan zijn SHA-256-hash en zet die in het beleid: 'sha256-…'. Dat werkt voor kleine fragmenten, zoals dat van een tagmanager, zonder nonces op te zetten.
3. Code naar bestanden verplaatsen
onclick="…"-handlers en scriptblokken in de HTML zijn de gebruikelijke reden om 'unsafe-inline' nodig te hebben. Verplaats ze naar .js-bestanden die vanaf je domein worden geserveerd en de noodzaak verdwijnt.
4. unsafe-eval verwijderen
Meestal eist een oude bibliotheek of de ontwikkelmodus van een framework die in productie is geslopen het. Controleer in de browserconsole wat erom vraagt en werk dat onderdeel bij of vervang het.
5. Uitrollen in rapportagemodus
Publiceer het nieuwe beleid eerst als Content-Security-Policy-Report-Only met een report-to- of report-uri-directive naar een eigen endpoint. Verzamel een paar dagen wat het zou blokkeren, stel bij, en zet het om naar de blokkerende header zodra de rapporten schoon zijn.
Controleer of het is opgelost
curl -sI https://jouwdomein.com | grep -i "content-security-policy"
In script-src mag noch 'unsafe-inline' noch 'unsafe-eval' voorkomen. Surf daarna met de console open door je site: elke blokkade verschijnt daar met de directive die hem veroorzaakte. De volledige theorie staat in HTTP-beveiligingsheaders uitgelegd voor mensen.
Veelgestelde vragen
Ik gebruik Tailwind of inline stijlen. Moet ik unsafe-inline uit style-src halen?
Wenselijk, maar niet dringend. Inline stijlen voeren geen code uit. Geef prioriteit aan script-src; een beleid met 'unsafe-inline' alleen in style-src beschermt tegen wat ertoe doet.
Google Tag Manager of een supportchat dwingt me het beleid te versoepelen.
Bijna allemaal hebben ze CSP-documentatie: ze accepteren een nonce of publiceren de hashes en domeinen die ze nodig hebben. Voeg hun origins toe aan script-src en connect-src in plaats van alles open te zetten.
Gaat de CSP mijn site breken?
Alleen als je hem op blokkeren zet zonder te testen. Daarvoor bestaat de rapportagemodus: die laat zien wat zou breken zonder het te breken.