jan-karel.nl

XXE Preventie

XXE Preventie

Websecurity Zonder Nablussen

Webrisico is zelden mysterieus. Het zit meestal in voorspelbare fouten die onder tijdsdruk blijven staan.

Bij XXE Preventie zit de meeste winst in veilige defaults die in elke release automatisch worden afgedwongen.

Dat maakt security minder een losse controle achteraf en meer een standaardkwaliteit van je product.

Directe maatregelen (15 minuten)

Waarom dit telt

De kern van XXE Preventie is risicoreductie in de praktijk. Technische context ondersteunt de maatregelkeuze, maar implementatie en borging staan centraal.

Verdediging: hoe je XML-parsers hun tanden trekt

Na al dit geweld is het tijd om te praten over hoe je jezelf beschermt. En het goede nieuws is: de verdediging tegen XXE is relatief eenvoudig. Het slechte nieuws is dat “eenvoudig” niet hetzelfde is als “wordt gedaan”.

Stap 1: Schakel DTD-processing uit

De meest effectieve verdediging is het volledig uitschakelen van DTD-processing. Geen DTD’s, geen entities, geen probleem:

Python (defusedxml):

import defusedxml.ElementTree as ET

# Veilig - blokkeert external entities en DTD's
tree = ET.parse('input.xml')

De defusedxml bibliotheek is een drop-in replacement voor Python’s standaard XML-bibliotheken die standaard alle gevaarlijke features uitschakelt.

Python (lxml):

from lxml import etree

parser = etree.XMLParser(
    resolve_entities=False,
    no_network=True,
    dtd_validation=False,
    load_dtd=False
)
tree = etree.parse('input.xml', parser)

Java:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

PHP:

// Sinds PHP 8.0 is dit de default, maar voor oudere versies:
libxml_disable_entity_loader(true);

Stap 2: Gebruik geen XML als het niet hoeft

Dit klinkt als een open deur, maar het is verbazingwekkend hoe vaak XML wordt gebruikt terwijl JSON prima zou volstaan. JSON heeft geen entities, geen DTD’s, en geen external references. Het is saai, voorspelbaar, en veilig. Precies wat je wilt van een dataformaat.

Als je kunt kiezen tussen XML en JSON voor een nieuwe API, kies JSON. Als je XML moet ondersteunen voor backward compatibility, zorg dan dat de parser geconfigureerd is alsof XML een potentieel gevaarlijk dier is – want dat is het.

Stap 3: Valideer en sanitize

Als je XML moet accepteren:

  • Blokkeer elk document dat een DOCTYPE-declaratie bevat
  • Weiger documenten met SYSTEM of PUBLIC entities
  • Beperk de entity-expansiediepte
  • Stel een maximum in voor de grootte van geexpandeerde content (tegen Billion Laughs)

Stap 4: Netwerksegmentatie

Zelfs als een parser kwetsbaar is, beperkt netwerksegmentatie de impact. Als de webserver geen uitgaand verkeer mag initiëren (behalve naar specifieke backend-services), kan OOB-exfiltratie niet plaatsvinden. De parser leest misschien het bestand, maar de data komt nergens.

Dit is defense in depth: meerdere lagen beveiliging die elk op zich onvoldoende zijn, maar samen een solide verdediging vormen.

Het ongemakkelijke gesprek over XML-parsers

Laten we even eerlijk zijn. Het feit dat XML external entities standaard aanstaan in de meeste parsers is een van de meest absurde ontwerpbeslissingen in de geschiedenis van software. Het is alsof je een auto verkoopt waarvan de deuren standaard niet op slot gaan, en dan in de handleiding schrijft: “Vergeet niet de deuren op slot te doen.”

De XML-specificatie is van 1998. External entities waren een feature. Ze waren bedoeld voor documentbeheer in grote organisaties, voor het hergebruiken van tekst in technische documentatie, voor het modulair opbouwen van complexe XML- structuren. Nobele doelen, allemaal. Maar de mensen die de specificatie schreven, werkten in een wereld waarin XML-documenten afkomstig waren van vertrouwde bronnen. Interne systemen. Collega’s.

Ze hadden niet voorzien dat in 2026 elke willekeurige bezoeker van een website XML kan aanleveren aan een server. Ze hadden niet voorzien dat die XML zou worden geparsed door een component dat standaard alles vertrouwt. Ze hadden niet voorzien dat een feature bedoeld voor documentbeheer zou worden misbruikt om wachtwoordbestanden te stelen.

En toch, nu we het weten – al meer dan vijftien jaar – staan external entities nog steeds standaard aan in talloze parsers. De documentatie zegt “schakel dit uit in productie”. Ontwikkelaars lezen de documentatie niet. De parser functioneert prima zonder configuratie. Het XML-document wordt correct geparsed. De unittests slagen. En ergens op een server worden bestanden gelezen door iedereen die weet hoe je een DOCTYPE schrijft.

Het is de triomf van backwards compatibility over gezond verstand. Het is de reden waarom we penetratietesters nodig hebben. En het is de reden waarom dit hoofdstuk bestaat.

Samenvatting

XXE is een kwetsbaarheid die voortkomt uit vertrouwen – vertrouwen in de input, vertrouwen in de parser, vertrouwen in de standaardconfiguratie. Het is een herinnering dat features en kwetsbaarheden soms hetzelfde zijn, afhankelijk van wie de XML schrijft.

De verdediging is eenvoudig: schakel uit wat je niet nodig hebt. Vertrouw geen input. Configureer je parsers alsof elke XML die binnenkomt geschreven is door iemand die je kwaad wil doen. Want op een dag is dat zo.

Referenties

Bron URL
OWASP XXE Prevention Cheat Sheet https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
PortSwigger Web Security Academy – XXE https://portswigger.net/web-security/xxe
PayloadsAllTheThings – XXE Injection https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XXE%20Injection
defusedxml (Python) https://github.com/tiran/defusedxml
W3C XML 1.0 Specification https://www.w3.org/TR/xml/
Billion Laughs Attack https://en.wikipedia.org/wiki/Billion_laughs_attack

Op de hoogte blijven?

Ontvang maandelijks cybersecurity-inzichten in je inbox.

← Webbeveiliging ← Home