Naar de inhoud
maarten.
Alle stories

XSS en CSRF voorkomen in webapplicaties

Maarten Soetens 13 min lezen

Cross-site scripting (XSS) en cross-site request forgery (CSRF) misbruiken verschillende eigenschappen van webapplicaties en browsers. Lees hoe deze aanvallen ontstaan, hoe u ze herkent en welke maatregelen passen bij de context van uw applicatie.

Het verschil tussen XSS en CSRF in een webapplicatie

XSS en CSRF worden vaak samen genoemd, maar de aanvaller benut bij elke aanval een ander probleem. Bij cross-site scripting kan een aanvaller code laten uitvoeren binnen de beveiligingscontext van een vertrouwde website. Dat gebeurt bijvoorbeeld doordat de applicatie gebruikersinvoer onveilig in een pagina plaatst. De browser behandelt de ingevoegde code vervolgens alsof die bij de website hoort. De aanvaller kan zo gegevens uitlezen die voor scripts toegankelijk zijn, acties uitvoeren namens de gebruiker of inhoud op de pagina aanpassen.

Bij cross-site request forgery probeert een aanvaller de browser van een ingelogde gebruiker juist een ongewenste aanvraag naar een website te laten sturen. De browser voegt vaak automatisch cookies of andere inloggegevens toe. De server ziet daardoor een geldig ingelogd account, terwijl de gebruiker de actie niet bewust heeft gestart. Denk aan het aanpassen van accountinstellingen of het toevoegen van een gebruiker.

Het onderscheid bepaalt welke verdediging nodig is. Output-encoding en veilige verwerking van HTML beperken XSS; CSRF-tokens en passende cookie-instellingen beperken vervalste aanvragen. Een CSRF-token voorkomt geen XSS: schadelijke code binnen de website kan vaak ook het token uitlezen. Andersom beschermt correcte output-encoding niet tegen een externe pagina die een aanvraag probeert te vervalsen. Beoordeel daarom zowel de verwerking van gegevens als de manier waarop de applicatie acties autoriseert.

Reflected, stored en DOM-based XSS herkennen

Reflected XSS ontstaat wanneer invoer uit een aanvraag vrijwel direct terugkomt in een antwoord van de server. Een zoekterm kan bijvoorbeeld in een foutmelding of paginatitel verschijnen. Als de applicatie die waarde zonder contextgeschikte encoding in HTML invoegt, kan een speciaal gemaakte link code laten uitvoeren zodra iemand de pagina opent. De kwaadaardige inhoud is meestal niet blijvend opgeslagen, maar kan via e-mail of een bericht worden verspreid.

Bij stored XSS bewaart de applicatie de invoer, bijvoorbeeld als reactie, profieltekst of supportbericht, en toont die later aan andere gebruikers. Daardoor kan één opgeslagen waarde meerdere bezoekers raken. De bron ligt niet noodzakelijk in een openbaar formulier: ook geïmporteerde gegevens, beheerdersnotities en inhoud uit externe systemen kunnen onveilig worden weergegeven. Controleer dus alle routes waar opgeslagen tekst terugkomt, niet alleen de invoerpagina.

DOM-based XSS ontstaat wanneer JavaScript in de browser onbetrouwbare gegevens verwerkt en die in een onveilige DOM-eigenschap plaatst. De server hoeft de schadelijke waarde daarbij niet in de HTML-response op te nemen. Een URL-fragment of queryparameter kan bijvoorbeeld via clientcode in een HTML-element belanden. Zoek tijdens codebeoordeling naar onveilige sinks zoals innerHTML en naar gegevensbronnen zoals URL-waarden. Het onderscheid tussen deze varianten helpt bij het vinden van de oorzaak, maar alle drie vragen om controle van gegevensstromen van bron tot uitvoer.

Output-encoding kiezen voor de juiste HTML-context

Output-encoding zet speciale tekens om zodat de browser ze als gegevens behandelt in plaats van als uitvoerbare markup of code. De juiste encoding hangt af van de plek waar een waarde terechtkomt. Voor gewone tekst in HTML encodeert een HTML-template doorgaans onder meer ampersands en punthaken. Een waarde in een HTML-attribuut vereist daarnaast bescherming tegen aanhalingstekens en moet in een correct gequote attribuut staan. Encoding voor HTML-tekst is niet automatisch veilig voor JavaScript, CSS of URL’s; elke context heeft eigen regels.

Gebruik daarom templates die standaard automatisch encoderen en schakel die bescherming niet uit om markup te laten verschijnen. Wanneer gegevens bewust als rijke HTML moeten worden getoond, is encoding alleen niet voldoende voor de gewenste functionaliteit. Pas dan een goed onderhouden sanitizer toe met een beperkte allowlist van elementen en attributen. Sta bijvoorbeeld niet zomaar event-attributen, onveilige URL-schema’s of willekeurige stijlregels toe. Een eenvoudige regex is geen betrouwbare HTML-sanitizer: de parser van de browser kent complexe uitzonderingen en herstelt ongeldige markup op manieren die filters kunnen omzeilen.

Encodeer zo laat mogelijk, op het moment van weergeven, en stem de bewerking af op de uitvoercontext. Zo blijft de oorspronkelijke waarde bruikbaar voor opslag en verwerking, terwijl elke weergave veilig wordt opgebouwd. Controleer ook frameworks en componenten die HTML als vertrouwde inhoud behandelen. Een expliciete escape-functie of een instelling zoals ‘raw HTML’ kan de standaardbescherming omzeilen. Leg zulke uitzonderingen vast en toets ze afzonderlijk.

DOM XSS voorkomen in client-side JavaScript

Clientcode kan XSS introduceren, ook als de server invoer zorgvuldig opslaat. Het risico ontstaat wanneer onbetrouwbare waarden vanuit bijvoorbeeld een queryparameter, URL-fragment, postMessage-bericht of API-antwoord in een uitvoerbare DOM-sink terechtkomen. innerHTML interpreteert een string als HTML; textContent plaatst diezelfde waarde als tekst. Voor gewone gebruikersinhoud is de tweede keuze doorgaans passend. Ook functies die HTML-parsen, attributen aanpassen of code dynamisch uitvoeren verdienen extra controle.

Begin met het in kaart brengen van bronnen en sinks. Volg welke waarden uit de URL, opslag, formulieren en berichtenstromen komen en waar ze uiteindelijk worden gebruikt. Gebruik waar mogelijk DOM-API’s die tekst als tekst behandelen, vaste templates en veilige componenten. Als de applicatie echt door gebruikers aangeleverde HTML moet tonen, sanitize die dan met een onderhouden bibliotheek en een expliciete allowlist. Een handmatige vervanging van enkele tekens houdt geen rekening met alle browserparsers en contexten.

Let ook op communicatie tussen vensters. Controleer bij postMessage het verwachte origin en valideer de inhoud van het bericht voordat die wordt gebruikt. Vertrouw niet op verborgen velden of client-side validatie als beveiligingsgrens; een gebruiker kan browsercode en aanvragen aanpassen. Een Content Security Policy kan de impact van bepaalde fouten beperken, maar herstelt een onveilige gegevensstroom niet. Combineer statische analyse en tests met handmatige beoordeling van code die dynamisch HTML samenstelt, vooral in routering, zoekfuncties en gedeelde UI-componenten.

Hoe CSRF misbruikmaakt van automatisch meegestuurde cookies

Een browser stuurt cookies vaak automatisch mee met aanvragen aan het domein waarvoor ze gelden. Dat gedrag maakt sessies gebruiksvriendelijk, maar kan ook CSRF mogelijk maken. Een aanvaller kan een gebruiker die is ingelogd bij een applicatie verleiden een andere pagina te bezoeken. Die pagina probeert vervolgens een aanvraag naar de applicatie te sturen. Als de browser de sessiecookie meestuurt en de server alleen controleert of er een geldige sessie bestaat, kan de aanvraag worden gezien als een actie van de ingelogde gebruiker.

De aanval werkt alleen wanneer aan bepaalde voorwaarden is voldaan. De gebruiker moet doorgaans een actieve sessie hebben en de doelactie moet vanuit de browser bereikbaar zijn. Bovendien moet de server onvoldoende controleren of de aanvraag bewust vanuit de eigen applicatie is gestart. Acties via GET zijn extra riskant wanneer ze gegevens wijzigen, omdat links, previews en crawlers zulke verzoeken kunnen activeren. Gebruik GET voor het opvragen van gegevens en passende methoden zoals POST voor wijzigingen, maar beschouw de HTTP-methode op zichzelf niet als CSRF-bescherming.

Ook aanvragen die moeilijker vanaf een externe pagina zijn op te bouwen, zijn niet automatisch veilig. Formulieren kunnen veel standaardverzoeken versturen en endpoints kunnen onverwachte contenttypes accepteren. Controleer daarom de volledige route: welke cookies worden meegestuurd, welke methode en headers zijn toegestaan, en welke validatie doet de server? CORS beperkt welke responses andere origins kunnen uitlezen; het voorkomt niet in algemene zin dat een browser een aanvraag verstuurt. Een beveiligingsmaatregel moet dus aansluiten op het precieze gedrag van de applicatie.

CSRF-tokens correct toepassen en valkuilen vermijden

Een CSRF-token is een onvoorspelbare waarde die de applicatie koppelt aan een sessie of gebruikerscontext. De server geeft het token beschikbaar aan een legitieme pagina en verwacht het bij aanvragen die gegevens wijzigen. Een andere website kan de sessiecookie doorgaans wel laten meesturen, maar kan het token niet zomaar uitlezen door de same-origin-beveiliging van de browser. De server vergelijkt het aangeleverde token met de verwachte waarde en weigert een ontbrekende of ongeldige waarde.

Tokens moeten server-side worden gecontroleerd op iedere relevante wijzigingsroute, niet alleen op formulieren die zichtbaar zijn in de interface. Een API-endpoint, alternatieve route of minder gebruikte beheerdersfunctie kan anders een achterdeur vormen. Plaats het token niet in een URL: URL’s belanden mogelijk in browsergeschiedenis, logs en verwijzende headers. Een verborgen formulierveld of een aangepaste requestheader is geschikter, mits de server de waarde daadwerkelijk valideert. Gebruik daarnaast voldoende onvoorspelbare tokens en voorkom dat caching een token van de ene gebruiker aan een andere gebruiker toont.

Een double-submit-cookiepatroon kan in bepaalde architecturen passen, maar vraagt om zorgvuldige binding tussen cookie en aanvraagwaarde en om bescherming tegen manipulatie van cookies. Beoordeel ook de interactie met XSS: code die al binnen de vertrouwde origin draait, kan vaak het token ophalen en gebruiken. CSRF-tokens beperken dus aanvallen vanaf andere origins, maar zijn geen vervanging voor XSS-preventie, autorisatiecontroles of validatie van de gevraagde actie. Controleer in tests expliciet dat ontbrekende, verlopen en vervalste tokens worden geweigerd.

SameSite-cookies, CORS en Content Security Policy

De cookie-instelling SameSite bepaalt onder welke omstandigheden een browser een cookie meestuurt bij cross-site-verzoeken. Strict beperkt dit sterk, maar kan navigatie- en inlogervaringen beïnvloeden. Lax is voor veel sessiecookies een bruikbare basis, al zijn er uitzonderingen, zoals bepaalde top-level navigaties met veilige methoden. None staat cross-site gebruik toe en vereist in moderne browsers doorgaans ook Secure. Kies de waarde op basis van de functies die de applicatie werkelijk nodig heeft, waaronder externe identiteitsproviders of ingebedde onderdelen.

SameSite is een aanvullende verdedigingslaag, geen universele vervanging voor CSRF-validatie. Browsergedrag en uitzonderingen kunnen verschillen, en applicaties kunnen meerdere cookies met uiteenlopende rollen gebruiken. Stel waar mogelijk ook Secure in zodat cookies alleen via HTTPS worden verzonden, en HttpOnly om directe toegang vanuit JavaScript te beperken. HttpOnly voorkomt niet dat XSS namens de gebruiker aanvragen uitvoert; het maakt alleen het uitlezen van de cookie lastiger.

CORS regelt of scripts op andere origins responses mogen lezen en welke cross-origin-aanvragen zijn toegestaan. Een eenvoudige aanvraag kan soms wel worden verstuurd zonder dat de aanvallende pagina de response kan uitlezen, dus CORS alleen blokkeert CSRF niet. Content Security Policy (CSP) kan de uitvoer van bepaalde scripts beperken en zo de impact van XSS verkleinen. Een CSP met nonce- of hash-gebaseerde scriptregels vraagt om een correcte configuratie; een ruime allowlist of onveilig gebruik van inline scripts verzwakt de werking. Behandel deze headers als lagen naast veilige code en servervalidatie.

XSS en CSRF testen in code, browser en productieconfiguratie

Een goede beveiligingstest kijkt verder dan één formulier of één payload. Breng eerst gegevensstromen in kaart: welke waarden komen van gebruikers, externe systemen, URL’s en browserberichten, waar worden ze opgeslagen en in welke contexten verschijnen ze? Test vervolgens varianten van dezelfde waarde in HTML-tekst, attributen, URL’s en client-side code. Controleer of speciale tekens als tekst worden weergegeven en of rich-text-inhoud alleen toegestane elementen en eigenschappen behoudt. Test ook foutmeldingen, zoekresultaten, beheerdersschermen en e-mailtemplates die later in een browser worden geopend.

Voor CSRF hoort een test vast te stellen dat elke wijzigingsroute zonder token, met een verkeerd token en met een token uit een andere sessie wordt geweigerd. Controleer daarnaast of wijzigingen via GET mogelijk zijn en of alternatieve API-routes dezelfde beveiliging toepassen. Een handmatige test vanuit een tweede origin kan zichtbaar maken welke cookies de browser meestuurt en of de server het verzoek toch accepteert. Automatiseer regressietests voor de beveiligingsregels, maar gebruik ze niet als bewijs dat alle browser- en applicatiecontexten zijn afgedekt.

Bekijk ook de configuratie die buiten de applicatiecode ligt: cookie-attributen, CSP, HTTPS en toegestane CORS-origins. Securityheaders kunnen in een testomgeving correct zijn en in productie ontbreken door een proxy of hostinglaag. Houd sanitizers en frameworks actueel en onderzoek kwetsbaarheden in afhankelijkheden die HTML verwerken. Bij een gevonden issue is het belangrijk de bron, de onveilige verwerking en alle vergelijkbare routes te onderzoeken. Zo voorkomt een gerichte reparatie dat dezelfde fout elders in de applicatie blijft bestaan.

Veelgestelde vragen

Hoe weet ik of mijn website al is misbruikt via XSS of CSRF?

Je kunt misbruik niet altijd met zekerheid uit serverlogs afleiden, maar afwijkende aanvragen en onverwachte wijzigingen kunnen aanwijzingen geven. Controleer logs op verdachte invoer, ongebruikelijke verzoekfrequenties en acties die niet passen bij het normale gebruik. Vergelijk die gegevens met wijzigingen in accounts, instellingen en inhoud. Houd er rekening mee dat een XSS-aanval ook vanuit de browser van een echte gebruiker kan plaatsvinden en daardoor op een normale aanvraag kan lijken.

  • Bewaar relevante logs en tijdstempels voordat je systemen aanpast.
  • Controleer of getroffen accounts of gegevens gewijzigd zijn.
  • Onderzoek meldingen van gebruikers en verdachte sessies.
  • Schakel bij vermoedelijke impact je incidentresponsproces in.

Wat moet ik doen als ik een XSS- of CSRF-kwetsbaarheid ontdek?

Beperk eerst het risico en herstel daarna de onderliggende fout, zonder bewijs of belangrijke gegevens te verliezen. Bepaal welke pagina’s, gebruikers en acties geraakt kunnen zijn en of de kwetsbaarheid al is misbruikt. Een tijdelijke maatregel, zoals het uitschakelen van een kwetsbare functie, kan nodig zijn terwijl een duurzame oplossing wordt voorbereid. Test de reparatie ook op andere routes die dezelfde component of gegevensverwerking gebruiken.

  • Leg de bevinding veilig vast en beperk toegang tot gevoelige informatie.
  • Herstel de oorzaak, niet alleen de specifieke testinvoer.
  • Beoordeel of sessies, wachtwoorden of gegevens opnieuw beveiligd moeten worden.
  • Informeer betrokken partijen volgens je incident- en meldprocedures.

Is een web application firewall genoeg om XSS en CSRF te voorkomen?

Nee, een web application firewall is geen afdoende vervanging voor veilige applicatiecode en servercontroles. Een firewall kan bekende aanvalspatronen herkennen en sommige verdachte aanvragen blokkeren, maar aanvallers kunnen filters omzeilen en geldige invoer kan afhankelijk van de context toch onveilig worden verwerkt. Ook kan een firewall niet zelfstandig bepalen of een gebruiker een specifieke wijziging bewust heeft aangevraagd.

  • Gebruik een firewall als aanvullende detectie- en verdedigingslaag.
  • Los onveilige gegevensverwerking in de applicatie zelf op.
  • Valideer autorisatie en aanvragen op de server.
  • Test of bestaande beveiligingsmaatregelen ook werken wanneer een firewallregel ontbreekt of wordt omzeild.

Wat is het verschil tussen CSRF en SSRF?

Bij CSRF wordt de browser van een gebruiker misleid om een actie uit te voeren bij een website waar die gebruiker is ingelogd; bij SSRF wordt een server misleid om zelf een aanvraag naar een andere bestemming te doen. CSRF misbruikt dus de browser en de gebruikerssessie, terwijl SSRF misbruikmaakt van netwerktoegang en vertrouwen aan de serverkant. De twee kwetsbaarheden vragen daarom om verschillende controles en zijn niet onderling uitwisselbaar.

  • Bescherm tegen CSRF door wijzigingsaanvragen aan de gebruikerscontext te koppelen en server-side te controleren.
  • Beperk bij SSRF welke bestemmingen en protocollen de server mag benaderen.
  • Controleer redirects en DNS-resolutie bij serveraanvragen.
  • Behandel interne netwerkadressen niet automatisch als veilig.

Hoe beveilig ik een legacy-webapplicatie tegen XSS en CSRF zonder alles opnieuw te bouwen?

Je kunt een legacy-applicatie stapsgewijs veiliger maken door eerst de meest risicovolle gegevensstromen en wijzigingsroutes aan te pakken. Begin met functies die gevoelige gegevens tonen of belangrijke accountacties uitvoeren. Voeg veilige uitvoerbehandeling toe op plekken waar gebruikersgegevens worden weergegeven en controleer wijzigingsaanvragen server-side. Een gedeeltelijke aanpak is nuttig, maar voorkom dat nieuwe uitzonderingen ontstaan die de bestaande bescherming omzeilen.

  • Maak een inventaris van invoervelden, templates, API-routes en clientcode.
  • Pak eerst publieke of veelgebruikte pagina’s en gevoelige acties aan.
  • Gebruik waar mogelijk centrale helpers of gedeelde componenten voor veilige verwerking.
  • Voeg regressietests toe zodat reparaties bij latere wijzigingen behouden blijven.
Portret van Maarten

Maarten

Freelance developer in Nijmegen

Even kennismaken?

Vertel kort wat er speelt. Dan hoor je wat er kan, wat ik anders zou doen en waar AI bij jou wél en niet iets toevoegt. Vrijblijvend.

hallo@maarten.online
Het kantoor in Nijmegen
© 2026 maarten.online Sitemap Privacy Algemene voorwaarden
Het kantoor in Nijmegen

Maarten.

Freelance developer in Nijmegen. Liever direct contact? Dat kan ook.

Kennismaken

Laat je gegevens achter, dan kijken we of het klikt. Vrijblijvend en zonder verkooppraat.

Maarten

Stuur een bericht via WhatsApp

Hoi! Waar kan ik je mee helpen?

nu