Hoe deze controle werkt
Scanaris loopt de HTML van je hoofdpagina door en let op twee soorten tags: <script src="..."> en <link rel="stylesheet" href="...">. Hij lost elke URL op tegen die van de pagina en beschouwt hem als extern als de origin (schema, domein en poort) van de jouwe verschilt; dat omvat URL's die met // beginnen, die met het protocol van de pagina van het CDN laden. data: en blob: negeert hij. Voor elke externe bron kijkt hij of het attribuut integrity aanwezig is; ontbreekt het, dan noteert hij hem. Is er minstens één, dan opent hij een bevinding met lage ernst met het aantal bronnen en de eerste tien URL's. Scripts en stijlen van je eigen domein worden niet gemeld: daar voegt SRI niets toe.
Waarom dit belangrijk is
Wanneer je een bibliotheek vanaf een CDN laadt, voer je in de browsers van je gebruikers uit wat dat CDN besluit te serveren. Wordt het CDN gecompromitteerd, of wisselt het domein van eigenaar, dan kan het bestand worden vervangen door een ander dat hetzelfde doet en bovendien formulieren steelt of naar oplichting doorstuurt. Dat gebeurde in juni 2024 met polyfill.io: het domein werd verkocht, het script begon kwaadaardige code te injecteren en meer dan honderdduizend sites die het opnamen, serveerden het zonder het te merken. Het attribuut integrity bevat de hash van het bestand dat jij hebt gecontroleerd; de browser hasht wat hij downloadt en weigert het uit te voeren als het niet overeenkomt. Het is lage ernst omdat het CDN eerst aangevallen moet worden, maar als dat gebeurt is de schade totaal en stil.
Hoe je het oplost
Optie 1: integrity aan de tags toevoegen
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"
integrity="sha384-..." crossorigin="anonymous"></script>
De hash krijg je door het exacte bestand te downloaden en dit uit te voeren:
curl -s https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js | openssl dgst -sha384 -binary | openssl base64 -A
cdnjs en jsDelivr tonen naast elk bestand een kant-en-klare hash om te kopiëren. Het attribuut crossorigin="anonymous" is verplicht zodat de browser een bron van een andere origin kan verifiëren. En de URL moet naar een vaste versie wijzen (@4.4.0), nooit naar latest: werkt het CDN het bestand bij, dan klopt de hash niet meer en laadt je site die bibliotheek niet meer, wat precies is wat je wilt bij een ongecontroleerde wijziging.
Optie 2: de bibliotheken vanaf je eigen domein serveren
Installeer ze met npm en laat je bundler (Vite, webpack, Nuxt, Next.js) ze in je build opnemen. Het bestand komt dan van je eigen server, met een hash in de naam, en SRI is niet meer nodig. Het is de gebruikelijke keuze in moderne projecten en degene die de bevinding bij de wortel oplost.
Wat geen SRI toelaat
De stylesheets van Google Fonts veranderen afhankelijk van de browser die ze opvraagt, dus hun hash is niet stabiel: óf je accepteert die bevinding, óf je host de lettertypen zelf (wat ook privacy en snelheid verbetert). Analytics- en advertentiescripts die zichzelf bijwerken (gtag.js, de Meta-pixel) verdragen evenmin een vaste hash; daar is de verdediging de CSP, die beperkt wat elke origin mag doen en die zwakke CSP meet.
Controleer of het opgelost is
curl -s https://jouwdomein.nl | grep -oE '<(script|link)[^>]*(src|href)="(https?:)?//[^"]+"[^>]*>' | grep -v integrity
Elke regel die eruit komt, is een externe bron zonder integrity. Blijven alleen die over die het niet kunnen hebben (lettertypen, analytics), dan ben je klaar.
Veelgestelde vragen
Vertraagt SRI het laden?
Niet merkbaar: een SHA-384-hash berekenen van een bestand van een paar honderd kilobyte duurt milliseconden. Wat het wel doet, is je dwingen versies vast te zetten, wat sowieso goede praktijk is.
Moet ik de hash veranderen als ik de bibliotheek bijwerk?
Ja, en dat is het punt: de update gaat door jouw handen. Gebruik je npm en je bundler, dan is het proces hetzelfde als altijd en regelt de build de hash.
En oude versies van bibliotheken op het CDN?
SRI garandeert dat het bestand het bestand is dat jij koos, niet dat het veilig is. Een jQuery 1.12 met de juiste hash heeft nog steeds zijn gebreken; dat detecteert verouderde bibliotheken.