libxml_disable_entity_loader()
Guida alla funzione libxml_disable_entity_loader() in PHP: protezione XXE, deprecazione in PHP 8.0 e alternative moderne sicure.
La funzione PHP libxml_disable_entity_loader() veniva utilizzata per attivare o disattivare il caricamento delle entità esterne per ogni documento XML analizzato dall'estensione libxml. Questa pagina spiega cosa faceva la funzione, l'attacco XML External Entity (XXE) da cui proteggeva, perché è stata rimossa e come scrivere codice di parsing XML sicuro in PHP moderno.
Importante: libxml_disable_entity_loader() è stata deprecata in PHP 8.0 e rimossa in PHP 8.1. Su PHP 8.1 o versioni successive, la sua chiamata genera un errore fatale. Utilizzare invece le alternative sicure descritte di seguito.
Cosa Faceva libxml_disable_entity_loader()
libxml_disable_entity_loader() era una funzione integrata di PHP che attivava o disattivava un flag globale all'interno di libxml — la libreria C che gestisce DOMDocument e SimpleXML in PHP. Quando il flag era attivo, libxml rifiutava di risolvere le entità esterne: riferimenti all'interno di un documento XML che puntano a una risorsa esterna, come un file locale (file:///etc/passwd) o un URL remoto.
Bloccare questa risoluzione era il metodo standard per difendersi dagli attacchi XML External Entity (XXE). In un attacco XXE, l'aggressore invia un payload XML il cui DOCTYPE dichiara un'entità che punta a una risorsa sensibile:
<?xml version="1.0"?>
<!DOCTYPE data [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<data>&xxe;</data>Se il parser risolve &xxe;, il contenuto di /etc/passwd finisce nel documento analizzato — che l'applicazione potrebbe poi restituire, registrare o memorizzare. Lo stesso trucco consente la server-side request forgery (SSRF, puntando l'entità a un URL interno) e il denial of service (la "billion laughs" bomb di espansione delle entità).
Sintassi
libxml_disable_entity_loader(bool $disable = true): boolParametri
| Parametro | Tipo | Descrizione |
|---|---|---|
$disable | bool | true disabilita il caricamento delle entità esterne; false lo riabilita. Il valore predefinito è true. |
Valore restituito
Restituisce il valore precedente del flag come booleano, in modo da poter ripristinare lo stato precedente dopo un singolo parsing.
Utilizzo Legacy
In PHP 7.x e versioni precedenti, proteggere un'operazione di parsing aveva questo aspetto. La chiamata è racchiusa in function_exists() in modo che lo stesso codice continui a funzionare su PHP 8.1+, dove la funzione non esiste più:
<?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 pattern più sicuro e comune era acquisire il valore precedente e ripristinarlo, in modo che la disabilitazione delle entità non si propagasse ad altri parsing nella stessa richiesta:
<?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);
?>Alternative Sicure in PHP Moderno
Due cambiamenti nel PHP moderno hanno reso la funzione superflua:
- libxml 2.9+ disabilita il caricamento delle entità esterne per impostazione predefinita. Da quella versione (inclusa con PHP da anni), le entità esterne DTD non vengono risolte a meno che non lo si richieda esplicitamente. Questo è il motivo per cui la funzione è diventata ridondante ed è stata rimossa.
LIBXML_NONETfornisce controllo per singola chiamata. Invece di modificare un flag globale, si passa un flag alla specifica chiamata di caricamento, che blocca l'accesso alla rete durante il parsing:
<?php
$doc = new DOMDocument();
// Parse without ever touching the network — no SSRF, no remote entities.
$doc->load('example.xml', LIBXML_NONET);
?>Se è necessario supportare i DTD ma si vuole comunque un rafforzamento della sicurezza, evitare LIBXML_DTDLOAD / LIBXML_NOENT su input non attendibili, poiché questi flag riabilitano proprio il comportamento su cui si basa XXE. Il valore predefinito più sicuro è semplicemente non passarli:
<?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);
?>Per ispezionare eventuali problemi di parsing invece di emettere avvisi grezzi, combinare il caricamento con libxml_use_internal_errors() e leggerli tramite libxml_get_errors().
Quando Usarla?
Si incontrerà libxml_disable_entity_loader() solo durante la lettura o la manutenzione di codice PHP 7 legacy. Per qualsiasi codice scritto oggi:
- Su PHP 8.1+, non è necessario fare nulla di speciale per le entità — il valore predefinito sicuro è già applicato. Aggiungere
LIBXML_NONETquando si vuole anche bloccare l'accesso alla rete. - Migrazione di codice vecchio? Sostituire
libxml_disable_entity_loader(true)con il flagLIBXML_NONETsu ogni caricamento, oppure racchiudere la chiamata infunction_exists()in modo che non abbia effetto sui nuovi runtime.
Conclusione
libxml_disable_entity_loader() era la difesa principale contro gli attacchi XXE, ma si basava su un flag globale fragile ed è stata rimossa da PHP 8.1. Il PHP moderno è sicuro per impostazione predefinita grazie a libxml 2.9+, e LIBXML_NONET offre un controllo esplicito per singolo parsing. Per ulteriori informazioni sullo stack XML di PHP, consultare la panoramica dell'estensione libxml e la guida al lavoro con il DOM XML.