# Prevenire gli XML External Entities (XXE) in PHP

> Prevenire l'XXE in PHP: che cosa vale da libxml 2.9 e PHP 8.0, quali flag sono rischiosi, LIBXML_NO_XXE da PHP 8.4 e come trovare gli ingressi XML.

Fonte: https://phpsos.de/it/blog/xxe-in-php/  
Stato: 25/10/2026  
Fornitore: PHP SOS, Assistenza di emergenza PHP e sicurezza dei server. Assistenza immediata su WhatsApp: https://wa.me/491736494914?text=Buongiorno%20PHP%20SOS%2C%20abbiamo%20bisogno%20di%20aiuto%20con%20il%20nostro%20server%20PHP.%20Azienda%3A%20%E2%80%A6%20Sito%20web%3A%20%E2%80%A6

**In breve:** l'XXE in PHP si previene caricando XML da fonti esterne senza le opzioni `LIBXML_NOENT`, `LIBXML_DTDLOAD` e `LIBXML_DTDVALID`, rifiutando i documenti con una DTD propria e impostando un entity loader che neghi le risorse esterne. Da libxml 2.9.0 PHP non sostituisce più le entità per impostazione predefinita, perciò `libxml_disable_entity_loader()` è obsoleta da PHP 8.0. Da PHP 8.4 con libxml 2.13 è disponibile inoltre `LIBXML_NO_XXE`.

## Una vecchia vulnerabilità che non scompare

Gli XML External Entities, in breve XXE, per molti sviluppatori sono un tema chiuso. Con le versioni moderne di libxml PHP ha disattivato per impostazione predefinita il caricamento di entità esterne, e la vecchia funzione di protezione è stata contrassegnata come obsoleta con PHP 8.0. Eppure le vulnerabilità XXE continuano a emergere nelle verifiche.

Il motivo sta quasi sempre in una di tre situazioni: qualcuno ha impostato un'opzione per risolvere un problema di importazione e ha così riattivato la sostituzione delle entità. Una libreria lo fa internamente. Oppure l'applicazione gira su un sistema legacy il cui PHP è stato compilato con una libxml molto vecchia.

L'XXE è pericoloso perché il parser XML carica contenuti che nel documento sono solo referenziati: file locali o indirizzi in rete. Possibili conseguenze sono la lettura di file di configurazione, richieste a sistemi interni simili a quelle dell'SSRF e il sovraccarico del server.

## Che cosa vale da libxml 2.9 e PHP 8.0

Il manuale PHP descrive la situazione così: da libxml 2.9.0 la sostituzione delle entità è disattivata per impostazione predefinita. Le entità esterne non devono quindi più essere disattivate espressamente, a meno che non si vogliano risolvere riferimenti a entità interne con determinate opzioni. Ne risulta questo quadro:

| Opzione | Effetto | Rischio con XML esterno |
|---|---|---|
| `LIBXML_NOENT` | Sostituisce le entità | Secondo il manuale può facilitare attacchi XXE |
| `LIBXML_DTDLOAD` | Carica il subset esterno | Attiva il caricamento di entità esterne |
| `LIBXML_DTDVALID` | Valida rispetto alla DTD | Può facilitare attacchi XXE |
| `LIBXML_NONET` | Vieta gli accessi di rete durante il caricamento | Opzione aggiuntiva protettiva |
| `LIBXML_NO_XXE` | Disattiva le entità esterne durante la sostituzione | Protettiva, da PHP 8.4 e libxml 2.13.0 |
| `LIBXML_PARSEHUGE` | Elimina i limiti interni del parser | Facilita il sovraccarico, solo per dati affidabili |

Il nome fuorviante di `LIBXML_NOENT` è una trappola nota: suona come «nessuna entità», ma ottiene l'opposto.

Per gli sviluppatori questo significa che non è rischioso il caricamento di XML in sé, ma ogni punto che modifica deliberatamente il comportamento predefinito. Una ricerca di queste costanti nel codice è quindi la via più rapida per una prima valutazione, ancor prima di qualsiasi verifica onerosa.

## Respingere l'XXE in PHP in sicurezza: uno schema per DOMDocument

L'esempio seguente combina più livelli di protezione. Imposta un entity loader che nega ogni risorsa esterna, carica senza opzioni rischiose e rifiuta i documenti con una DTD propria:

```php
<?php
declare(strict_types=1);

function ladeFremdesXml(string $xml): DOMDocument
{
    // Negare in via di principio le risorse esterne
    libxml_set_external_entity_loader(
        static fn (?string $public, string $system, array $context) => null
    );

    $optionen = LIBXML_NONET;
    if (defined('LIBXML_NO_XXE')) {
        $optionen |= LIBXML_NO_XXE;
    }

    $vorher = libxml_use_internal_errors(true);
    $dom = new DOMDocument();
    $ok = $dom->loadXML($xml, $optionen);
    libxml_use_internal_errors($vorher);

    if (!$ok) {
        throw new InvalidArgumentException('XML ungültig.');
    }
    if ($dom->doctype !== null) {
        throw new InvalidArgumentException('Dokumente mit DTD werden nicht angenommen.');
    }
    return $dom;
}
```

Secondo il manuale PHP, un valore di ritorno `null` nel loader blocca il caricamento della risorsa esterna. OWASP segnala che non basta passare `null` direttamente a `libxml_set_external_entity_loader()`; serve una funzione che restituisca `null`. Per SimpleXML e XMLReader valgono le stesse opzioni. Dove possibile, OWASP raccomanda di rifiutare del tutto le DTD.

> **Consiglio:**
> Se può determinare lei stessa il formato dei dati, per lo scambio tra sistemi propri JSON è di solito la scelta più semplice. Per formati esterni come SAML o file Office, XML resta inevitabile.

## Trovare gli ingressi XML nascosti

Molte applicazioni elaborano più XML di quanto pensino i loro sviluppatori. Scorra questo elenco:

1. **Interfacce:** client e server SOAP, XML-RPC, servizi di pagamento e spedizione con risposte XML.
2. **Accesso:** il single sign-on con SAML si basa su documenti XML provenienti dall'esterno.
3. **Importazioni:** dati di prodotto, feed, sitemap, formati di catalogo.
4. **Upload:** le immagini SVG sono XML; i formati Office come DOCX e XLSX sono composti internamente da file XML.
5. **Librerie:** pacchetti Composer per documenti, tabelle o SAML che usano libxml internamente.

Nel codice aiutano due ricerche:

```bash
grep -rnE "LIBXML_(NOENT|DTDLOAD|DTDVALID|PARSEHUGE)" src/ vendor/ --include="*.php"
grep -rnE "(loadXML|simplexml_load_(string|file)|XMLReader|->load\()" src/ --include="*.php"
```

Le occorrenze in `vendor/` non sono motivo di panico, ma un'occasione per verificare versione e avvisi di sicurezza della libreria.

Con gli upload SVG l'XXE è solo uno dei vari rischi, perché i file SVG possono contenere anche script. Se la sua applicazione non ha assolutamente bisogno del formato, non lo ammetta affatto per gli upload. Altrimenti elabori gli SVG solo con lo schema sicuro sopra e fornisca i file in modo che il browser non esegua gli script contenuti.

Faccia attenzione anche ai messaggi di errore. Se un'applicazione restituisce senza filtro agli utenti i messaggi di libxml, vi possono comparire parti del contenuto elaborato. Gli errori appartengono al log, l'utente riceve un messaggio generico; nell'esempio sopra `libxml_use_internal_errors(true)` fa sì che gli avvisi non finiscano nell'output. Limiti inoltre la dimensione dei documenti XML ricevuti, prima che il parser li veda.

## Sistemi legacy: quando manca la protezione predefinita

La protezione offerta dal comportamento predefinito dipende dalla versione di libxml con cui PHP è stato compilato. Quale versione giri lo mostra `php -r 'echo LIBXML_DOTTED_VERSION, PHP_EOL;'` da riga di comando o l'output di `phpinfo()` in un ambiente protetto. Verifichi il valore per ogni versione di PHP sul server, perché CLI e server web possono usare build diverse. Le immagini container portano con sé una libxml propria; lì si verifica nel container in esecuzione, non sull'host.

Sono particolarmente a rischio i sistemi che girano comunque su vecchie versioni di PHP. Per PHP 8.1 e precedenti, dopo PHP2Shell non esistono più aggiornamenti ufficiali del progetto PHP; che cosa significhi lo descrive [PHP 8.1 e precedenti: che cosa vale ora](https://phpsos.de/it/php2shell/php-8-1-und-aelter/). Un sistema legacy con libxml obsoleta e vulnerabilità aperte nell'interprete andrebbe non solo messo in sicurezza nel codice, ma migrato. I rischi affini nell'elaborazione di dati esterni li descrive [Deserializzazione insicura in PHP](https://phpsos.de/it/ratgeber/deserialisierung-php/), le chiamate a destinazioni interne [Prevenire il Server-Side Request Forgery in PHP](https://phpsos.de/it/blog/ssrf-in-php/).

## Come aiuta concretamente PHP SOS?

- Troviamo tutti i punti in cui la sua applicazione e le sue librerie elaborano XML da fonti esterne.
- Rimuoviamo le opzioni rischiose del parser e inseriamo una funzione di caricamento centrale e messa in sicurezza.
- Verifichiamo le versioni di libxml e PHP su tutti i server e pianifichiamo la migrazione per i sistemi legacy.
- Testiamo le modifiche con documenti di prova innocui e documentiamo il risultato.

La verifica del software proprio è descritta nel servizio [audit di sicurezza e penetration test](https://phpsos.de/it/leistungen/sicherheitsaudit-penetrationstest/).


## Domande frequenti

**Le applicazioni PHP sono protette automaticamente dall'XXE da PHP 8 in poi?**  
Nel caso standard in larga misura sì, perché libxml dalla versione 2.9.0 non sostituisce più le entità. La protezione viene meno però non appena il codice imposta opzioni come LIBXML_NOENT, LIBXML_DTDLOAD o LIBXML_DTDVALID, oppure se PHP è compilato con una versione molto vecchia di libxml.

**Devo ancora richiamare libxml_disable_entity_loader?**  
No. La funzione è obsoleta da PHP 8.0, perché il caricamento di entità esterne è disattivato per impostazione predefinita. Per una protezione aggiuntiva il manuale PHP raccomanda libxml_set_external_entity_loader e, da PHP 8.4 con una libxml adatta, la costante LIBXML_NO_XXE.

**Dove la mia applicazione elabora XML, in realtà?**  
Spesso in punti inattesi: interfacce SOAP, single sign-on con SAML, feed RSS, sitemap, importazioni di prodotti, upload SVG e file Office, che internamente sono composti da XML. Anche le librerie possono elaborare XML senza che sia visibile nel proprio codice.

**Che cos'è l'attacco Billion Laughs?**  
Un documento con entità annidate che nella risoluzione cresce enormemente e consuma memoria e tempo di calcolo. Appartiene alla stessa famiglia dell'XXE. Offrono protezione la disattivazione della sostituzione delle entità e il rifiuto dei documenti con una DTD propria.

## Fonti

- [Manuale PHP: libxml_disable_entity_loader](https://www.php.net/manual/de/function.libxml-disable-entity-loader.php)
- [Manuale PHP: costanti libxml](https://www.php.net/manual/de/libxml.constants.php)
- [OWASP XML External Entity Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html)
