libxml_disable_entity_loader()
Découvrez la fonction libxml_disable_entity_loader() en PHP, son rôle contre les attaques XXE et les alternatives sécurisées en PHP moderne.
La fonction PHP libxml_disable_entity_loader() permettait d'activer ou de désactiver le chargement des entités externes pour tout document XML analysé par l'extension libxml. Cette page explique ce que faisait cette fonction, l'attaque XML External Entity (XXE) contre laquelle elle protégeait, pourquoi elle a été supprimée, et comment écrire du code d'analyse XML sécurisé en PHP moderne.
Important : libxml_disable_entity_loader() a été dépréciée en PHP 8.0 et supprimée en PHP 8.1. Si vous utilisez PHP 8.1 ou une version plus récente, l'appeler provoque une erreur fatale. Utilisez plutôt les alternatives sécurisées décrites ci-dessous.
Ce que faisait libxml_disable_entity_loader()
libxml_disable_entity_loader() était un utilitaire PHP intégré qui activait ou désactivait un indicateur global dans libxml — la bibliothèque C qui alimente DOMDocument et SimpleXML en PHP. Lorsque cet indicateur était activé, libxml refusait de résoudre les entités externes : des références dans un document XML pointant vers une ressource extérieure, comme un fichier local (file:///etc/passwd) ou une URL distante.
Bloquer cette résolution était la méthode standard pour se défendre contre les attaques XML External Entity (XXE). Dans une attaque XXE, l'attaquant soumet une charge XML dont le DOCTYPE déclare une entité pointant vers une ressource sensible :
<?xml version="1.0"?>
<!DOCTYPE data [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<data>&xxe;</data>Si l'analyseur résout &xxe;, le contenu de /etc/passwd se retrouve dans le document analysé — que l'application pourrait ensuite afficher, journaliser ou stocker. La même technique permet la falsification de requêtes côté serveur (SSRF, en pointant l'entité vers une URL interne) et le déni de service (la bombe d'expansion d'entités « billion laughs »).
Syntaxe
libxml_disable_entity_loader(bool $disable = true): boolParamètres
| Paramètre | Type | Description |
|---|---|---|
$disable | bool | true désactive le chargement des entités externes ; false le réactive. Vaut true par défaut. |
Valeur de retour
Retourne la valeur précédente de l'indicateur sous forme de booléen, ce qui permettait de restaurer l'état antérieur après une seule analyse.
Utilisation historique
En PHP 7.x et antérieur, sécuriser une analyse ressemblait à ceci. L'appel est enveloppé dans function_exists() afin que le même code continue de fonctionner sur PHP 8.1+, où la fonction n'existe plus :
<?php
// Disable external entities (deprecated in PHP 8.0, removed in 8.1).
if (function_exists('libxml_disable_entity_loader')) {
libxml_disable_entity_loader(true);
}
// Load an XML file into a DOMDocument object.
$doc = new DOMDocument();
if (!$doc->load('example.xml')) {
die('Failed to load XML file.');
}
?>Un schéma courant et plus sûr consistait à capturer la valeur précédente et à la restaurer, afin que la désactivation des entités ne se propage pas à d'autres analyses dans la même requête :
<?php
$previous = libxml_disable_entity_loader(true);
$doc = new DOMDocument();
$doc->loadXML($untrustedXml);
// Restore the global flag for the rest of the request.
libxml_disable_entity_loader($previous);
?>Alternatives sécurisées en PHP moderne
Deux évolutions du PHP moderne ont rendu cette fonction inutile :
- libxml 2.9+ désactive le chargement des entités externes par défaut. Depuis cette version (livrée avec PHP depuis des années), les entités externes DTD ne sont pas résolues, sauf si vous le demandez explicitement. C'est pourquoi la fonction est devenue redondante et a été supprimée.
LIBXML_NONEToffre un contrôle par appel. Au lieu de basculer un indicateur global, vous passez un drapeau à l'appel de chargement spécifique, ce qui bloque l'accès réseau pendant l'analyse :
<?php
$doc = new DOMDocument();
// Parse without ever touching the network — no SSRF, no remote entities.
$doc->load('example.xml', LIBXML_NONET);
?>Si vous devez prendre en charge les DTD tout en conservant un renforcement de sécurité, évitez LIBXML_DTDLOAD / LIBXML_NOENT sur des entrées non fiables, car ces drapeaux réactivent précisément le comportement dont dépend XXE. La valeur par défaut la plus sûre consiste simplement à ne pas les passer :
<?php
$doc = new DOMDocument();
// Default flags (0): no entity substitution, no DTD loading from untrusted XML.
$doc->loadXML($untrustedXml);
// SimpleXML follows the same secure default.
$xml = simplexml_load_string($untrustedXml);
?>Pour inspecter les problèmes d'analyse au lieu d'émettre des avertissements bruts, combinez le chargement avec libxml_use_internal_errors() et lisez-les via libxml_get_errors().
Quand utiliser cette fonction ?
Vous ne rencontrerez libxml_disable_entity_loader() qu'en lisant ou en maintenant du code PHP 7 hérité. Pour tout code que vous écrivez aujourd'hui :
- Sur PHP 8.1+, ne faites rien de spécial pour les entités — le comportement sécurisé par défaut s'applique déjà. Ajoutez
LIBXML_NONETsi vous souhaitez également bloquer l'accès réseau. - Vous migrez du code ancien ? Remplacez
libxml_disable_entity_loader(true)par le drapeauLIBXML_NONETsur chaque chargement, ou enveloppez l'appel dansfunction_exists()pour qu'il n'ait aucun effet sur les nouvelles versions.
Conclusion
libxml_disable_entity_loader() était autrefois la défense privilégiée contre les attaques XXE, mais elle reposait sur un indicateur global fragile et a été supprimée depuis PHP 8.1. Le PHP moderne est sécurisé par défaut grâce à libxml 2.9+, et LIBXML_NONET vous offre un contrôle explicite par analyse. Pour en savoir plus sur la pile XML de PHP, consultez la vue d'ensemble de l'extension libxml et travailler avec le DOM XML.