Signatur
Beschreibung
libxml_set_external_entity_loader() ermöglicht es, den internen Mechanismus von libxml zum Laden externer Entities durch eine eigene PHP-Funktion zu ersetzen. Immer wenn libxml eine externe Entity auflösen muss (z. B. eine DTD oder eine externe XML-Datei), wird statt des Standard-Loaders die registrierte Callback-Funktion aufgerufen.
Die Callback-Funktion erhält drei Parameter: $public (den öffentlichen Bezeichner der Entity, oft null), $system (den System-Bezeichner, also die URL oder den Dateipfad) und $context (ein Array mit Kontextinformationen des XML-Parsers). Die Funktion muss entweder eine Ressource (einen geöffneten Stream), einen String (den Inhalt der Entity), oder null zurückgeben, um das Laden abzubrechen.
Besonders wichtig ist diese Funktion im Sicherheitskontext: Mit ihr lässt sich das Laden beliebiger externer Entities gezielt blockieren, was zum Schutz gegen XXE-Angriffe (XML External Entity Injection) unerlässlich ist. Durch Übergabe von null als Argument wird der Standard-Loader wiederhergestellt.
Typische Einsatzgebiete sind das sichere Parsen von nicht vertrauenswürdigen XML-Dokumenten, das selektive Erlauben bestimmter externer Ressourcen sowie das Umleiten von Entity-Anfragen auf lokale Dateien oder Caches.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $resolver_function Pflicht | callable|null | Eine Callback-Funktion mit der Signatur function(?string $public, string $system, array $context): resource|string|null, die zur Auflösung externer Entities verwendet wird. Gibt null zurück, um das Laden zu blockieren. Wird null als Argument übergeben, wird der Standard-Loader von libxml wiederhergestellt. |
Rückgabewert
true zurück, wenn der Loader erfolgreich gesetzt wurde, andernfalls false.Beispiele
XXE-Angriffe blockieren durch Deaktivieren des Entity-Loaders
<?php
// Alle externen Entities blockieren – Schutz gegen XXE-Injection
libxml_set_external_entity_loader(function (?string $public, string $system, array $context): ?string {
// Lädt keine externe Entity – gibt null zurück, um den Vorgang abzubrechen
trigger_error(
"Externes Entity-Laden blockiert: system='$system'",
E_USER_WARNING
);
return null;
});
$xml = <<<XML
<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>
XML;
$doc = new DOMDocument();
$doc->loadXML($xml, LIBXML_NONET);
echo $doc->documentElement->textContent; // Gibt nichts aus – Entity wurde blockiert
Externe Entity auf lokalen Cache umleiten
<?php
// Bestimmte DTD-Anfragen auf lokale Dateien umleiten
libxml_set_external_entity_loader(function (?string $public, string $system, array $context) {
$allowed = [
'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd' => '/var/cache/dtd/xhtml1-strict.dtd',
];
if (isset($allowed[$system]) && file_exists($allowed[$system])) {
return fopen($allowed[$system], 'r');
}
// Alle anderen externen Entities blockieren
return null;
});
$xml = file_get_contents('/var/www/html/document.xhtml');
$doc = new DOMDocument();
$doc->loadXML($xml);
echo "XML erfolgreich geladen: " . $doc->documentElement->nodeName;
Standard-Loader wiederherstellen
<?php
// Zuvor gesetzten Loader entfernen und Standard-Loader wiederherstellen
libxml_set_external_entity_loader(null);
echo "Standard-Loader wiederhergestellt.";
// Wichtig · Fallstricke
Sicherheitshinweis – XXE-Injection: Das Laden externer XML-Entities ist eine der häufigsten Angriffsvektoren in PHP-Anwendungen, die XML verarbeiten. Ohne Schutzmaßnahmen können Angreifer über speziell präparierte XML-Dokumente beliebige lokale Dateien auslesen (z. B. /etc/passwd) oder interne Netzwerkressourcen ansprechen (SSRF). Es wird dringend empfohlen, libxml_set_external_entity_loader() einzusetzen und alle externen Entities standardmäßig zu blockieren.
Ab PHP 8.0 ist der externe Entity-Loader standardmäßig deaktiviert, wenn kein Loader gesetzt ist, und gibt eine Warnung aus. In PHP 7.x und darunter musste dieser Schutz manuell aktiviert werden.
Die Funktion wirkt global auf alle libxml-Operationen im aktuellen PHP-Prozess, also auch auf SimpleXML, DOMDocument, XMLReader und xsl_xsltprocessor. Der Callback sollte daher so defensiv wie möglich implementiert werden.
Beim Einsatz in einer Umgebung mit mehreren gleichzeitigen Requests (z. B. Swoole, ReactPHP) ist besondere Vorsicht geboten, da der Loader prozessglobal gesetzt wird und Race Conditions auftreten können.