
Een strikte CSP instellen: hergebruik nooit dezelfde nonce

Genereer voor iedere HTTP-respons een nieuwe, onvoorspelbare nonce en zet die in zowel de CSP-header als de script-elementen die je wilt toestaan, zoals de CSP-handleiding van MDN beschrijft. Binnen één respons mogen vertrouwde scripts dezelfde waarde gebruiken; bij de volgende respons moet die veranderen. Voeg de nonce toe via het vertrouwde HTML-sjabloon, zodat een geïnjecteerd script-element niet automatisch toestemming krijgt.
Een basisbeleid voor dynamische HTML luidt: Content-Security-Policy: script-src 'nonce-{waarde}' 'strict-dynamic'; object-src 'none'; base-uri 'none';. Vervang {waarde} tijdens het opbouwen van de respons. In het HTML-attribuut staat alleen de waarde, zonder het voorvoegsel nonce-. Zo kan een gekozen inline script draaien zonder 'unsafe-inline'; rechtstreeks opgenomen externe scripts hebben onder dit beleid eveneens een passende nonce of hash nodig.
Genereer de waarde bij iedere respons
Maak de nonce aan tijdens de verwerking van de respons, niet bij het starten van de applicatie. De CSP-specificatie van W3C vereist een nieuwe waarde telkens wanneer het beleid wordt verzonden en adviseert minstens 128 bits uit een cryptografisch veilige generator. In een Express-route levert crypto.randomBytes(16).toString('base64') die hoeveelheid willekeurige gegevens op: 16 bytes zijn 128 bits.
Gebruik vervolgens dezelfde variabele voor de header en het sjabloon. Bouw de waarde voor script-src op als 'nonce-' gevolgd door de gegenereerde nonce, verzend het beleid met res.setHeader('Content-Security-Policy', beleid) en geef de nonce aan res.render('pagina', { nonce }) door. Een bedoeld script-element in het sjabloon kan dan bijvoorbeeld worden weergegeven als <script nonce="{{nonce}}" src="/app.js"></script>. De accolades zijn hier een plaatsaanduiding voor de sjabloonvariabele; de uiteindelijke HTML bevat de gegenereerde waarde.
Controleer ook hoe HTML wordt gecachet. Als een cache de volledige pagina met header en script-attributen opnieuw uitserveert, levert zij dezelfde nonce opnieuw af. Genereer voor dynamische pagina’s de header en HTML daarom samen bij elke levering, of kies voor onveranderlijke, gecachete HTML een hashbeleid. Alleen de header vernieuwen werkt niet: de waarde in de toegestane script-elementen moet binnen die respons overeenkomen.
Ken de nonce alleen toe aan bedoelde scripts

Plaats het nonce-attribuut op de plekken waar het applicatiesjabloon vertrouwde script-elementen maakt. Dat geldt voor een bewust opgenomen inline script en voor een extern script dat rechtstreeks in de HTML staat. De keuze welke scripts mogen draaien, hoort dus in het sjabloon of de component die de pagina opbouwt.
Vermijd een tekstvervanging die achteraf ieder script-element in de volledige HTML van een nonce voorziet. Als onvertrouwde inhoud dan al een script-element heeft ingevoegd, krijgt ook dat element de geldige waarde. Houd gebruikersinhoud buiten de sjabloononderdelen die een nonce ontvangen en blijf die inhoud passend escapen of saneren; CSP is een extra verdedigingslaag tegen scriptinjectie.
Een nonce op een script-element staat andere vormen van inline JavaScript niet automatisch toe. Een onclick-attribuut en een javascript:-URL blijven onder dit beleid geblokkeerd. Verplaats zulke handelingen naar een toegestaan script met addEventListener. Onderzoek ook gebruik van eval() en vergelijkbare uitvoering van code uit een tekenreeks, want die kan door script-src worden geblokkeerd.
Controleer de scripts die vertrouwde code verder laadt

Met 'strict-dynamic' kan een vertrouwd script nieuwe script-elementen maken en laden zonder elk daarvan afzonderlijk een nonce te geven. Die overdracht begint bij een script dat rechtstreeks in de HTML staat en zelf een passende nonce of hash heeft. Een domein op een toelatingslijst vervangt in browsers die 'strict-dynamic' toepassen de nonce op dat oorspronkelijke script niet.
Bekijk daarom vóór het toestaan van een extern script welke andere scripts het binnenhaalt en hoe het hun URL’s bepaalt. Gebruikt vertrouwde code onvertrouwde invoer voor de URL van een nieuw script-element, dan kan 'strict-dynamic' ook die laadactie toestaan. Geef een extern script niet uitsluitend een nonce om CSP-meldingen te laten verdwijnen: de code kan daarmee tevens bepalen welke scripts daarna worden geladen. Herhaal de controle na wijzigingen in die externe code.
Controleer de routes waarop zulke scripts werkelijk voorkomen. Een widget die pas na een klik wordt geladen, verschijnt mogelijk niet tijdens een snelle controle van de eerste paginalading. Let ook op een script dat via meerdere sjablonen wordt ingevoegd: elk rechtstreeks opgenomen exemplaar moet in zijn eigen respons onder het beleid passen.
Gebruik hashes voor statische HTML

Voor statisch geleverde of volledig gecachete HTML past een hash beter: de scriptinhoud blijft gelijk, terwijl een nonce bij iedere respons in zowel de header als de HTML zou moeten veranderen. De uitleg van web.dev over strikte CSP koppelt nonce-beleid aan servergerenderde pagina’s en hashbeleid aan statische of gecachete HTML.
Bereken voor ieder toegestaan inline script de Base64-gecodeerde SHA-256-hash van de exacte scriptinhoud en neem die op in script-src als 'sha256-{hash}'. De browser berekent de hash van het ontvangen script en voert het alleen uit als de waarden overeenkomen. Een wijziging in de inhoud, zelfs in witruimte, vraagt om een nieuwe hash en een bijgewerkt beleid. Een statische build kan beide tegelijk publiceren.
Wil je een rechtstreeks opgenomen extern script met een hash toestaan, dan heeft het script-element ook een overeenkomend integrity-attribuut nodig. Dat maakt het onderhoud anders dan bij een nonce: verandert het externe bestand, dan moeten de betrokken hashwaarden mee veranderen. Voor scripts waarvan de inhoud vaak wijzigt, verdient de keuze tussen een statische hash en een dynamisch opgebouwd beleid daarom aandacht.
Rol het beleid uit met Report-Only
Verzend het voorgenomen beleid op representatieve routes eerst als Content-Security-Policy-Report-Only. De browser toont overtredingen in de ontwikkelaarsconsole en kan ze naar een ingesteld rapporteeradres sturen, terwijl de beleidsregels de scripts nog niet blokkeren. Gebruik hiervoor een HTTP-header; een CSP in een meta-element ondersteunt Report-Only niet.
Doorloop verschillende sjablonen, ingelogde weergaven en functies die pas na een handeling scripts laden. Herleid elke relevante melding tot het betrokken script of de functie: voeg een ontbrekende nonce toe in het specifieke vertrouwde sjabloon, vervang een inline handler of onderzoek een dynamisch geladen afhankelijkheid. Schakel over naar Content-Security-Policy zodra de bedoelde functies onder het voorgenomen beleid werken en controleer ze ook na die omschakeling.
Loop daarbij de fouten na die een geldig ogende header toch onveilig of onbruikbaar maken:
- Vraag dezelfde dynamische pagina opnieuw op en controleer dat de nieuwe respons een andere nonce heeft.
- Vergelijk binnen iedere respons de waarde in script-src met het nonce-attribuut van elk bedoeld script-element.
- Controleer dat gebruikersinhoud geen nonce krijgt en dat een cache geen complete HTML-respons met een oude waarde opnieuw levert.
- Loop rechtstreeks opgenomen externe scripts en de scripts die zij later laden afzonderlijk na.
- Los relevante Report-Only-meldingen op voordat het beleid de uitvoering daadwerkelijk afdwingt.
Lees ook:
Gerelateerde artikelen


DMARC veilig invoeren: p=reject mag niet je eerste stap zijn

Framer of Webflow: snelheid wint pas zolang je CMS klein blijft

AI-crawlers blokkeren: robots.txt is een verzoek, geen slot

Van reCAPTCHA naar Turnstile: v3 past niet in de compatibiliteitsmodus

Vercel of Cloudflare Pages: snelheid beslist niet, dataverkeer wel
Abonneer je op onze nieuwsbrief
Ontvang het laatste nieuws over Web3, AI en crypto rechtstreeks in je inbox.