libxml_disable_entity_loader()
Aprende sobre la función libxml_disable_entity_loader() en PHP, cómo protege contra ataques XXE y las alternativas modernas recomendadas.
La función PHP libxml_disable_entity_loader() se utilizaba para activar o desactivar la carga de entidades externas en todos los documentos XML analizados por la extensión libxml. Esta página explica lo que hacía la función, el ataque de Entidad Externa XML (XXE) contra el que protegía, por qué fue eliminada y cómo escribir código de análisis XML seguro en PHP moderno.
Importante: libxml_disable_entity_loader() fue obsoleta en PHP 8.0 y eliminada en PHP 8.1. Si usas PHP 8.1 o superior, llamarla genera un error fatal. Usa las alternativas seguras que se muestran a continuación.
Qué hacía libxml_disable_entity_loader()
libxml_disable_entity_loader() era una utilidad PHP integrada que alternaba un indicador global dentro de libxml — la biblioteca C que impulsa DOMDocument y SimpleXML de PHP. Cuando el indicador estaba activado, libxml se negaba a resolver entidades externas: referencias dentro de un documento XML que apuntan a un recurso externo, como un archivo local (file:///etc/passwd) o una URL remota.
Bloquear esa resolución era la forma estándar de defenderse contra los ataques de Entidad Externa XML (XXE). En un ataque XXE, el atacante envía un payload XML cuyo DOCTYPE declara una entidad que apunta a un recurso sensible:
<?xml version="1.0"?>
<!DOCTYPE data [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<data>&xxe;</data>Si el analizador resuelve &xxe;, el contenido de /etc/passwd termina en el documento analizado, que la aplicación podría luego mostrar, registrar o almacenar. El mismo truco permite la falsificación de solicitudes del lado del servidor (SSRF, apuntando la entidad a una URL interna) y la denegación de servicio (la bomba de expansión de entidades "billion laughs").
Sintaxis
libxml_disable_entity_loader(bool $disable = true): boolParámetros
| Parámetro | Tipo | Descripción |
|---|---|---|
$disable | bool | true desactiva la carga de entidades externas; false la reactiva. El valor predeterminado es true. |
Valor de retorno
Devuelve el valor anterior del indicador como un booleano, de modo que se podía restaurar el estado previo después de un único análisis.
Uso heredado
En PHP 7.x y versiones anteriores, asegurar un análisis se veía así. La llamada está envuelta en function_exists() para que el mismo código siga funcionando en PHP 8.1+, donde la función ya no existe:
<?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 patrón más seguro y común era capturar el valor anterior y restaurarlo, para que la desactivación de entidades no se propagara a otros análisis no relacionados en la misma solicitud:
<?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);
?>Alternativas seguras en PHP moderno
Dos cambios en PHP moderno hicieron que la función fuera innecesaria:
- libxml 2.9+ desactiva la carga de entidades externas por defecto. Desde esa versión (incluida con PHP durante años), las entidades externas DTD no se resuelven a menos que lo solicites explícitamente. Por eso la función se volvió redundante y fue eliminada.
LIBXML_NONETproporciona control por llamada. En lugar de alternar un indicador global, se pasa un indicador a la llamada de carga específica, lo que bloquea el acceso a la red durante el análisis:
<?php
$doc = new DOMDocument();
// Parse without ever touching the network — no SSRF, no remote entities.
$doc->load('example.xml', LIBXML_NONET);
?>Si debes admitir DTDs pero aún quieres mayor protección, evita LIBXML_DTDLOAD / LIBXML_NOENT con entradas no confiables, ya que esos indicadores reactivan el comportamiento del que depende XXE. El valor predeterminado más seguro es simplemente no pasarlos:
<?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);
?>Para inspeccionar los problemas de análisis en lugar de emitir advertencias directas, combina la carga con libxml_use_internal_errors() y léelos mediante libxml_get_errors().
¿Cuándo usaría esto?
Solo encontrarás libxml_disable_entity_loader() al leer o mantener código PHP 7 heredado. Para cualquier código que escribas hoy:
- En PHP 8.1+, no hagas nada especial para las entidades — el valor predeterminado seguro ya se aplica. Agrega
LIBXML_NONETcuando también quieras bloquear el acceso a la red. - ¿Migrando código antiguo? Reemplaza
libxml_disable_entity_loader(true)con el indicadorLIBXML_NONETen cada carga, o envuelve la llamada enfunction_exists()para que no haga nada en los entornos de ejecución nuevos.
Conclusión
libxml_disable_entity_loader() fue durante mucho tiempo la defensa principal contra los ataques XXE, pero dependía de un indicador global frágil y ha sido eliminada desde PHP 8.1. PHP moderno es seguro por defecto gracias a libxml 2.9+, y LIBXML_NONET te ofrece control explícito por análisis. Para más información sobre la pila XML de PHP, consulta la descripción general de la extensión libxml y trabajar con el DOM XML.