# XML External Entities (XXE) vältimine PHP-s

> XXE vältimine PHP-s: mis kehtib libxml 2.9 ja PHP 8.0 järel, millised lipud on riskantsed, LIBXML_NO_XXE alates PHP 8.4-st ja kuidas XML-sisendeid leida.

Allikas: https://phpsos.de/et/blog/xxe-in-php/  
Seis: 25.10.2026  
Pakkuja: PHP SOS, PHP hädaabi ja serveri turvalisus. Kohene abi WhatsAppis: https://wa.me/491736494914?text=Tere%2C%20PHP%20SOS%2C%20vajame%20abi%20oma%20PHP-serveriga.%20Ettev%C3%B5te%3A%20%E2%80%A6%20Veebisait%3A%20%E2%80%A6

**Lühidalt:** XXE-d hoiate PHP-s ära nii, et laadite võõrastest allikatest pärit XML-i ilma valikuteta `LIBXML_NOENT`, `LIBXML_DTDLOAD` ja `LIBXML_DTDVALID`, lükkate tagasi oma DTD-ga dokumendid ning seate Entity-Loaderi, mis keeldub välistest ressurssidest. libxml 2.9.0-st alates PHP entity'id vaikimisi enam ei asenda, seetõttu on `libxml_disable_entity_loader()` PHP 8.0-st vananenud. PHP 8.4-st koos libxml 2.13-ga on lisaks saadaval `LIBXML_NO_XXE`.

## Vana auk, mis ei kao

XML External Entities ehk XXE loetakse paljude arendajate jaoks lahendatud teemaks. PHP on kaasaegsete libxml-i versioonidega väliste entity'de laadimise vaikimisi välja lülitanud ja vana kaitsefunktsioon märgiti PHP 8.0-ga vananenuks. Sellest hoolimata ilmuvad XXE-augud kontrollides edasi.

Põhjus peitub peaaegu alati ühes kolmest olukorrast: keegi on seadnud valiku impordiprobleemi lahendamiseks ja lülitanud sellega entity asendamise uuesti sisse. Teek teeb seda seespidiselt. Või töötab rakendus vanal süsteemil, mille PHP on ehitatud väga vana libxml-iga.

XXE on ohtlik, sest XML-parser laadib seejuures sisu, millele dokumendis ainult viidatakse: kohalikud failid või aadressid võrgus. Võimalikud tagajärjed on konfiguratsioonifailide väljalugemine, päringud sisesüsteemidele sarnaselt SSRF-iga ja serveri ülekoormamine.

## Mis kehtib libxml 2.9 ja PHP 8.0 järel

PHP käsiraamat kirjeldab olukorda nii: libxml 2.9.0-st on entity'de asendamine vaikimisi välja lülitatud. Väliseid entity'id ei pea seetõttu enam eraldi välja lülitama, välja arvatud juhul, kui sisemised entity viited tuleb teatud valikutega lahendada. Sellest tuleneb see ülevaade:

| Valik | Mõju | Risk võõra XML-i puhul |
|---|---|---|
| `LIBXML_NOENT` | asendab entity'id | käsiraamatu kohaselt võib XXE-rünnakuid hõlbustada |
| `LIBXML_DTDLOAD` | laadib välise alamhulga | aktiveerib väliste entity'de laadimise |
| `LIBXML_DTDVALID` | valideerib DTD vastu | võib XXE-rünnakuid hõlbustada |
| `LIBXML_NONET` | keelab laadimisel võrguühendused | kaitsev lisavalik |
| `LIBXML_NO_XXE` | lülitab asendamisel välised entity'id välja | kaitsev, PHP 8.4-st ja libxml 2.13.0-st |
| `LIBXML_PARSEHUGE` | tühistab parseri sisemised piirid | hõlbustab ülekoormamist, ainult usaldusväärsete andmete jaoks |

`LIBXML_NOENT` eksitav nimi on tuntud lõks: see kõlab nagu „ei mingeid entity'id“, kuid toimib vastupidiselt.

Arendajatele tähendab see: riskantne ei ole XML-i laadimine iseenesest, vaid iga koht, mis vaikekäitumist teadlikult muudab. Nende konstantide otsing koodis on seetõttu kiireim tee esimese hinnanguni, enne iga töömahukat kontrolli.

## XXE turvaline tõrje PHP-s: muster DOMDocumenti jaoks

Järgmine näide ühendab mitu kaitsekihti. See seab Entity-Loaderi, mis keeldub igast välisest ressursist, laeb ilma riskantsete valikuteta ja lükkab tagasi oma DTD-ga dokumendid:

```php
<?php
declare(strict_types=1);

function ladeFremdesXml(string $xml): DOMDocument
{
    // Väliseid ressursse põhimõtteliselt mitte lubada
    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;
}
```

PHP käsiraamatu kohaselt blokeerib tagastusväärtus `null` loaderis välise ressursi laadimise. OWASP juhib tähelepanu, et ei piisa, kui anda `null` otse funktsioonile `libxml_set_external_entity_loader()`; vaja on funktsiooni, mis tagastab `null`. SimpleXML-i ja XMLReaderi puhul kehtivad samad valikud. Kus iganes võimalik, soovitab OWASP DTD-d täielikult tagasi lükata.

> **Näpunäide:**
> Kui saate andmevormingu ise määrata, on JSON oma süsteemide vaheliseks andmevahetuseks tavaliselt lihtsam valik. Võõraste vormingute, nagu SAML või Office-failid, puhul jääb XML vältimatuks.

## Peidetud XML-sisendite leidmine

Paljud rakendused töötlevad rohkem XML-i, kui nende arendajad arvavad. Käige see nimekiri läbi:

1. **Liidesed:** SOAP-kliendid ja -serverid, XML-RPC, makse- ja saatmisteenused XML-vastustega.
2. **Sisselogimine:** SAML-iga ühekordne sisselogimine põhineb XML-dokumentidel, mis tulevad väljastpoolt.
3. **Impordid:** tooteandmed, voogud, saidikaardid, kataloogivormingud.
4. **Üleslaadimised:** SVG-pildid on XML; Office-vormingud, nagu DOCX ja XLSX, koosnevad seespidiselt XML-failidest.
5. **Teegid:** Composer-paketid dokumentide, tabelite või SAML-i jaoks, mis kasutavad seespidiselt libxml-i.

Koodis aitavad kaks otsingut:

```bash
grep -rnE "LIBXML_(NOENT|DTDLOAD|DTDVALID|PARSEHUGE)" src/ vendor/ --include="*.php"
grep -rnE "(loadXML|simplexml_load_(string|file)|XMLReader|->load\()" src/ --include="*.php"
```

Leiud kaustas `vendor/` ei ole põhjus paanikaks, kuid põhjus kontrollida teegi versiooni ja turvateateid.

SVG-üleslaadimiste puhul on XXE ainult üks mitmest riskist, sest SVG-failid võivad sisaldada ka skripte. Kui teie rakendus seda vormingut tingimata ei vaja, ärge lubage seda üleslaadimisteks üldse. Muul juhul töötlege SVG-d ainult ülaltoodud turvalise mustriga ja edastage failid nii, et brauser sisalduvaid skripte ei käivita.

Pöörake tähelepanu ka veateadetele. Kui rakendus tagastab libxml-i teated kasutajatele filtreerimata, võivad neis olla osad töödeldud sisust. Vead kuuluvad logisse, kasutaja saab üldise teate; ülaltoodud näites hoolitseb `libxml_use_internal_errors(true)` selle eest, et hoiatused väljundisse ei satuks. Piirake lisaks vastuvõetud XML-dokumentide suurust, enne kui parser neid üldse näeb.

## Vanad süsteemid: millal vaikekaitse puudub

Vaikekäitumise kaitse sõltub libxml-i versioonist, millega PHP on ehitatud. Millist versiooni kasutatakse, näitab `php -r 'echo LIBXML_DOTTED_VERSION, PHP_EOL;'` käsureal või `phpinfo()` väljund kaitstud keskkonnas. Kontrollige väärtust iga PHP versiooni jaoks serveris, sest CLI ja veebiserver võivad kasutada erinevaid ehitusi. Konteineripildid toovad kaasa oma libxml-i; seal kontrollige töötavas konteineris, mitte hostis.

Eriti mõjutatud on süsteemid, mis niikuinii töötavad vanadel PHP versioonidel. PHP 8.1 ja vanemate jaoks ei ole pärast PHP2Shelli PHP projektilt enam ametlikke uuendusi; mida see tähendab, kirjeldab [PHP 8.1 ja vanemad: mis nüüd kehtib](https://phpsos.de/et/php2shell/php-8-1-und-aelter/). Vana süsteem aegunud libxml-i ja avatud interpretaatoriaukudega tuleks mitte ainult koodis kaitsta, vaid migreerida. Seotud riskid võõraste andmete töötlemisel kirjeldab [Ebaturvaline deserialiseerimine PHP-s](https://phpsos.de/et/ratgeber/deserialisierung-php/), sisemiste sihtkohtade päringuid [Server-Side Request Forgery vältimine PHP-s](https://phpsos.de/et/blog/ssrf-in-php/).

## Kuidas PHP SOS konkreetselt aitab?

- Me leiame kõik kohad, kus teie rakendus ja selle teegid töötlevad võõrastest allikatest pärit XML-i.
- Me eemaldame riskantsed parseri valikud ja ehitame sisse kesksed, kaitstud laadimisfunktsioonid.
- Me kontrollime libxml-i ja PHP versioone kõigis serverites ning planeerime vanade süsteemide puhul migratsiooni.
- Me testime muudatusi kahjutute kontrolldokumentidega ja dokumenteerime tulemuse.

Oma tarkvara kontrolli kirjeldab teenus [Turvaaudit ja läbitungimistest](https://phpsos.de/et/leistungen/sicherheitsaudit-penetrationstest/).


## Korduma kippuvad küsimused

**Kas PHP-rakendused on PHP 8-st alates XXE eest automaatselt kaitstud?**  
Vaikejuhul suures osas, sest libxml ei asenda entity'id enam alates versioonist 2.9.0. Kaitse langeb aga ära, niipea kui kood seab valikuid nagu LIBXML_NOENT, LIBXML_DTDLOAD või LIBXML_DTDVALID või kui PHP on ehitatud väga vana libxml-i versiooniga.

**Kas peaksin libxml_disable_entity_loader'it veel kutsuma?**  
Ei. Funktsioon on PHP 8.0-st vananenud, sest väliste entity'de laadimine on vaikimisi välja lülitatud. Lisakaitseks soovitab PHP käsiraamat funktsiooni libxml_set_external_entity_loader ning alates PHP 8.4-st sobiva libxml-iga konstanti LIBXML_NO_XXE.

**Kus minu rakendus üldse XML-i töötleb?**  
Sageli ootamatutes kohtades: SOAP-liidesed, SAML-iga ühekordne sisselogimine, RSS-voogud, saidikaardid, tooteimpordid, SVG-üleslaadimised ja Office-failid, mis koosnevad seespidiselt XML-ist. Ka teegid võivad XML-i töödelda, ilma et see oleks oma koodis nähtav.

**Mis on Billion-Laughs-rünnak?**  
Dokument pesastatud entity'dega, mis lahendamisel kasvab tohutult ning tarbib mälu ja arvutusaega. See kuulub samasse perekonda mis XXE. Kaitse pakuvad entity asendamise väljalülitamine ja oma DTD-ga dokumentide tagasilükkamine.

## Allikad

- [PHP käsiraamat: libxml_disable_entity_loader](https://www.php.net/manual/de/function.libxml-disable-entity-loader.php)
- [PHP käsiraamat: libxml-i konstandid](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)
