Praegune olukord: PHP2Shell. Kaks kriitilist PHP turvaauku (CVSS 9,2) lubavad kahjulikku koodi ilma sisselogimata. Paigad alates 21.10.2026.Mida nüüd teha →

XML External Entities (XXE) vältimine PHP-s

Kuidas arendajad töötlevad XML-i PHP-s turvaliselt: libxml-i vaikekäitumine, riskantsed valikud nagu LIBXML_NOENT, kaitse Entity-Loaderi ja LIBXML_NO_XXE-ga ning tüüpilised peidetud XML-sisendid.

Seis Lugemisaeg 5 minTurvaline arendus

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
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:

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. 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, sisemiste sihtkohtade päringuid Server-Side Request Forgery vältimine PHP-s.

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.

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

Kohene kontakt

Iga tund loeb. Kirjutage meile kohe.

Kirjeldage lühidalt süsteemi, PHP versiooni ja tähelepanekut. Võtame teiega ühendust, selgitame olukorra välja ja töötame kaugtööna, kuni intsident on lõpetatud ja dokumenteeritud.

Kohene abi