Wat is XSS en hoe bescherm je je applicatie?
Auteur: Redactie Domeny.ForSale Gepubliceerd
Kort samengevat: XSS (Cross-Site Scripting) is een kwetsbaarheid waardoor de browser onvertrouwde code uitvoert in de context van de bezochte website. Dit ontstaat meestal wanneer een applicatie gebruikersgegevens behandelt als onderdeel van de pagina of als code, in plaats van als gewone tekst. Bescherming draait vooral om het veilig weergeven van gegevens, niet alleen om het blokkeren van verdachte invoer.
Hoe XSS werkt en wat een aanvaller kan doen
De browser interpreteert HTML als de structuur van de pagina en JavaScript als uit te voeren instructies. Als een applicatie ongecontroleerde inhoud invoegt op een plek waar die als code wordt geïnterpreteerd, kan een aanvaller de werking van de pagina bij een andere gebruiker beïnvloeden. Zulke inhoud kan afkomstig zijn uit een reactie, een adresparameter, een profielnaam of een antwoord van een externe dienst.
Code die via XSS wordt uitgevoerd, werkt in de context van de aangevallen website. Die code kan beschikbare gegevens op de pagina lezen, de inhoud veranderen, een vals inlogformulier tonen of handelingen uitvoeren met de rechten van de ingelogde gebruiker. De daadwerkelijke gevolgen hangen af van de functies van de applicatie en de toegepaste beveiliging.
Dit betekent niet automatisch dat de server of de hele computer wordt overgenomen. Het probleem zit vooral in het vertrouwen dat de browser stelt in code die door de betreffende website wordt uitgevoerd. Een gebruiker kan het juiste adres openen en toch op een pagina met kwaadaardige inhoud terechtkomen.
De drie meest beschreven vormen van XSS
Deze indeling helpt vast te stellen waar de gevaarlijke inhoud vandaan komt en waar de applicatie die verwerkt:
- Stored XSS, oftewel opgeslagen XSS: de inhoud wordt opgeslagen, bijvoorbeeld in een database met reacties. Het probleem ontstaat wanneer de applicatie die inhoud op een onveilige manier aan anderen toont. Alleen het opslaan in een database is nog geen kwetsbaarheid.
- Reflected XSS, oftewel gereflecteerde XSS: de applicatie haalt gegevens uit een verzoek, bijvoorbeeld een zoekterm uit een URL, en neemt die zonder passende bescherming op in het antwoord. Een aanvaller kan het slachtoffer ertoe proberen te bewegen een speciaal gemaakte link te openen.
- DOM-based XSS: de onveilige verwerking vindt plaats in JavaScript die in de browser wordt uitgevoerd. DOM staat voor Document Object Model: een weergave van de pagina die scripts kunnen uitlezen en wijzigen.
Deze begrippen sluiten elkaar niet altijd uit. ‘Opgeslagen’ en ‘gereflecteerd’ beschrijven vooral hoe de gegevens worden aangeleverd, terwijl ‘DOM-based’ aangeeft waar de onveilige verwerking plaatsvindt. Ook inhoud die in een database is opgeslagen, kan tot DOM-based XSS leiden.
Eenvoudig voorbeeld: tekst behandeld als HTML
Stel dat een applicatie een gebruikersnaam weergeeft. Voer het volgende voorbeeld alleen uit in je eigen testomgeving of op een plek waarvoor je uitdrukkelijk toestemming hebt:
const naam = '<img src="x" onerror="alert(\'XSS-test\')">';
const element = document.querySelector('#begroeting');
element.innerHTML = naam;
De eigenschap innerHTML laat de browser de waarde interpreteren als HTML. Als de afbeelding niet wordt geladen, kan de foutafhandeling alert uitvoeren: er verschijnt dan een venster met een melding. Dit demonstreert het uitvoeren van code, niet het stelen van gegevens. Aanvullende beveiliging van de browser of de website kan dit blokkeren.
Als de naam gewone tekst moet zijn, is dit de juiste oplossing:
const naam = '<img src="x" onerror="alert(\'XSS-test\')">';
const element = document.querySelector('#begroeting');
element.textContent = naam;
De eigenschap textContent geeft de opgegeven tekenreeks weer zonder een afbeelding aan te maken en zonder de instructie erin uit te voeren. Het verschil is wezenlijk: gegevens blijven gegevens. Je hoeft niet te raden welke woorden of tags gevaarlijk kunnen zijn.
Hoe bescherm je een applicatie tegen XSS?
Stem de bescherming af op de plek waar de gegevens worden gebruikt. Uitvoercodering houdt in dat speciale tekens zo worden weergegeven dat ze niet als syntaxis worden geïnterpreteerd. Voor tekst in HTML gelden andere regels dan voor een attribuutwaarde, een URL of een fragment JavaScript. Alleen de punthaken < en > vervangen biedt niet in al deze situaties bescherming.
Gebruik voor gewone tekst veilige templatemechanismen en interfaces zoals textContent. Automatische codering in een framework helpt, maar kan worden omzeild met een functie die onbewerkte HTML invoegt. Zulke uitzonderingen vereisen een afzonderlijke controle.
Als gebruikers daadwerkelijk opmaak met HTML nodig hebben, gebruik dan een actief onderhouden bibliotheek voor sanitisatie, zoals DOMPurify. Sanitisatie verwijdert niet-toegestane elementen en attributen volgens vastgestelde regels. Een zelfgemaakt filter op basis van een paar reguliere expressies biedt geen gelijkwaardige bescherming.
Validatie, oftewel controleren of gegevens een toegestaan formaat hebben, blijft nuttig: een leeftijdsveld moet een getal accepteren en een linkadres moet een toegestaan schema gebruiken. Validatie vervangt echter niet het veilig weergeven van gegevens. Alleen het formulier beveiligen is ook niet genoeg, want gegevens kunnen via andere routes in de applicatie terechtkomen.
Aanvullende beveiligingslagen beperken de gevolgen van fouten. Met CSP (Content Security Policy) kun je regels voor het uitvoeren van scripts vastleggen, maar de effectiviteit hangt af van de configuratie. Het attribuut HttpOnly voorkomt dat JavaScript een cookie uitleest, maar verhindert niet dat kwaadaardige code allerlei handelingen binnen de actieve sessie uitvoert.
Waar begin je met het controleren van je eigen website?
Volg één veld vanaf de ontvangst van de gegevens tot elke plek waar ze worden weergegeven. Een reactie kan onder een artikel veilig zijn, maar in het moderatiepaneel of het voorbeeld van een melding onveilig. Controleer ook waar innerHTML wordt gebruikt en waar functies worden ingezet die automatische codering omzeilen.
De volgende stap is een test in een geïsoleerde omgeving: stel vast of de ingevoerde inhoud als tekst verschijnt of de structuur van de pagina verandert. Voeg na de correctie een regressietest toe die detecteert of deze gegevens opnieuw als HTML worden behandeld.
Zie ook: Wat is 2FA?
