Aktuális helyzet: PHP2Shell. Két kritikus PHP-rés (CVSS 9,2) lehetővé teszi kártékony kód futtatását bejelentkezés nélkül. Javítások 2026. 10. 21. óta.Mi a teendő most →

XML External Entities (XXE) megelőzése PHP-ban

Hogyan dolgozzák fel a fejlesztők biztonságosan az XML-t PHP-ban: a libxml alapértelmezett viselkedése, kockázatos opciók, mint a LIBXML_NOENT, védelem entity loaderrel és LIBXML_NO_XXE-vel, valamint tipikus rejtett XML-bemenetek.

Állapot 6 perc olvasási időBiztonságos fejlesztés

Röviden: Az XXE-t PHP-ban úgy előzi meg, hogy idegen forrásból származó XML-t a LIBXML_NOENT, a LIBXML_DTDLOAD és a LIBXML_DTDVALID opció nélkül tölt be, elutasítja a saját DTD-t tartalmazó dokumentumokat, és olyan entity loadert állít be, amely megtagadja a külső erőforrásokat. A libxml 2.9.0 óta a PHP alapértelmezés szerint már nem helyettesíti az entityket, ezért a libxml_disable_entity_loader() a PHP 8.0 óta elavult. A PHP 8.4-től, libxml 2.13-mal, emellett rendelkezésre áll a LIBXML_NO_XXE.

Egy régi rés, amely nem tűnik el

Az XML External Entities, röviden XXE, sok fejlesztő szemében lezárt téma. A PHP a modern libxml-verziókkal alapértelmezés szerint kikapcsolta a külső entityk betöltését, a régi védelmi függvényt pedig a PHP 8.0-val elavultként jelölték meg. Mégis továbbra is felbukkannak XXE-rések az ellenőrzések során.

Az ok szinte mindig három helyzet egyike: valaki beállított egy opciót egy importprobléma megoldására, és ezzel újra bekapcsolta az entity-helyettesítést. Egy könyvtár ezt belül teszi meg. Vagy az alkalmazás egy régi rendszeren fut, amelynek PHP-ját nagyon régi libxml-lel fordították.

Az XXE azért veszélyes, mert az XML-értelmező ilyenkor olyan tartalmakat tölt be, amelyekre a dokumentum csak hivatkozik: helyi fájlokat vagy hálózati címeket. Lehetséges következmények a konfigurációs fájlok kiolvasása, az SSRF-hez hasonló kérések belső rendszerek felé, és a szerver túlterhelése.

Mi érvényes a libxml 2.9 és a PHP 8.0 óta

A PHP kézikönyve így írja le a helyzetet: a libxml 2.9.0-tól az entityk helyettesítése alapértelmezés szerint ki van kapcsolva. A külső entityket ezért már nem kell külön kikapcsolni, kivéve, ha belső entity-hivatkozásokat akarnak bizonyos opciókkal feloldani. Ebből a következő áttekintés adódik:

Opció Hatás Kockázat idegen XML-nél
LIBXML_NOENT helyettesíti az entityket a kézikönyv szerint megkönnyítheti az XXE-támadásokat
LIBXML_DTDLOAD betölti a külső subsetet aktiválja a külső entityk betöltését
LIBXML_DTDVALID a DTD-vel szemben validál megkönnyítheti az XXE-támadásokat
LIBXML_NONET tiltja a hálózati hozzáférést betöltéskor védő kiegészítő opció
LIBXML_NO_XXE a helyettesítésnél kikapcsolja a külső entityket védő, a PHP 8.4-től és a libxml 2.13.0-tól
LIBXML_PARSEHUGE feloldja az értelmező belső határait megkönnyíti a túlterhelést, csak megbízható adatokhoz

A LIBXML_NOENT félrevezető neve ismert csapda: „nincs entity”-nek hangzik, de az ellenkezőjét eredményezi.

A fejlesztők számára ez azt jelenti: nem az XML betöltése önmagában kockázatos, hanem minden hely, amely tudatosan megváltoztatja az alapértelmezett viselkedést. Ezeknek a konstansoknak a keresése a kódban ezért a leggyorsabb út az első értékeléshez, még bármilyen költséges vizsgálat előtt.

XXE biztonságos elhárítása PHP-ban: egy minta a DOMDocumenthez

A következő példa több védelmi réteget kombinál. Olyan entity loadert állít be, amely minden külső erőforrást megtagad, kockázatos opciók nélkül tölt be, és elutasítja a saját DTD-t tartalmazó dokumentumokat:

<?php
declare(strict_types=1);

function ladeFremdesXml(string $xml): DOMDocument
{
    // A külső erőforrásokat alapvetően megtagadni
    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;
}

A PHP kézikönyve szerint a null visszatérési érték a loaderben blokkolja a külső erőforrás betöltését. Az OWASP rámutat, hogy nem elég a null-t közvetlenül átadni a libxml_set_external_entity_loader() függvénynek; olyan függvény kell, amely null-t ad vissza. A SimpleXML-re és az XMLReaderre ugyanezek az opciók érvényesek. Ahol csak lehet, az OWASP a DTD-k teljes elutasítását ajánlja.

Tipp

Ha az adatformátumot Ön határozhatja meg, a saját rendszerek közötti cseréhez a JSON többnyire az egyszerűbb választás. Idegen formátumoknál, mint a SAML vagy az Office-fájlok, az XML elkerülhetetlen marad.

Rejtett XML-bemenetek megtalálása

Sok alkalmazás több XML-t dolgoz fel, mint amennyit a fejlesztői gondolnak. Menjen végig ezen a listán:

  1. Interfészek: SOAP-kliensek és -szerverek, XML-RPC, XML-válaszokat adó fizetési és szállítási szolgáltatások.
  2. Bejelentkezés: a SAML-lel történő single sign-on kívülről érkező XML-dokumentumokra épül.
  3. Importok: termékadatok, feedek, sitemapek, katalógusformátumok.
  4. Feltöltések: az SVG-képek XML-ek; az Office-formátumok, mint a DOCX és az XLSX, belül XML-fájlokból állnak.
  5. Könyvtárak: dokumentumokhoz, táblázatokhoz vagy SAML-hez használt Composer-csomagok, amelyek belül a libxml-t használják.

A kódban két keresés segít:

grep -rnE "LIBXML_(NOENT|DTDLOAD|DTDVALID|PARSEHUGE)" src/ vendor/ --include="*.php"
grep -rnE "(loadXML|simplexml_load_(string|file)|XMLReader|->load\()" src/ --include="*.php"

A vendor/ mappában lévő találatok nem ok a pánikra, de alkalom arra, hogy ellenőrizze a könyvtár verzióját és biztonsági közleményeit.

Az SVG-feltöltéseknél az XXE csak az egyik kockázat a több közül, mert az SVG-fájlok szkripteket is tartalmazhatnak. Ha az alkalmazásának nincs feltétlenül szüksége a formátumra, egyáltalán ne engedélyezze a feltöltésekhez. Különben az SVG-t csak a fenti biztonságos mintával dolgozza fel, és úgy szolgálja ki a fájlokat, hogy a böngésző a bennük lévő szkripteket ne hajtsa végre.

Ügyeljen a hibaüzenetekre is. Ha egy alkalmazás a libxml üzeneteit szűretlenül adja vissza a felhasználóknak, azokban a feldolgozott tartalom részei állhatnak. A hibák a naplóba valók, a felhasználó általános üzenetet kap; a fenti példában a libxml_use_internal_errors(true) gondoskodik arról, hogy a figyelmeztetések ne kerüljenek a kimenetbe. Korlátozza ezenkívül a kapott XML-dokumentumok méretét, mielőtt az értelmező egyáltalán látná őket.

Régi rendszerek: mikor hiányzik az alapértelmezett védelem

Az alapértelmezett viselkedés általi védelem azon a libxml-verzión múlik, amellyel a PHP-t fordították. Hogy melyik verzió fut, azt a php -r 'echo LIBXML_DOTTED_VERSION, PHP_EOL;' mutatja a parancssorban, vagy a phpinfo() kimenete egy védett környezetben. Ellenőrizze az értéket a szerveren lévő minden PHP-verzióra, mert a CLI és a webszerver eltérő buildeket használhat. A konténerképek saját libxml-t hoznak magukkal; ott a futó konténerben ellenőrizzen, nem a hoston.

Különösen érintettek azok a rendszerek, amelyek amúgy is régi PHP-verziókon futnak. A PHP 8.1-re és régebbiekre a PHP2Shell után nincs több hivatalos frissítés a PHP-projekttől; hogy ez mit jelent, azt a PHP 8.1 és régebbiek: mi érvényes most írja le. Egy elavult libxml-lel és nyitott értelmezőréssekkel rendelkező régi rendszert nem csak a kódban érdemes biztosítani, hanem migrálni kell. Az idegen adatok feldolgozásának rokon kockázatait a Nem biztonságos deszerializáció PHP-ban írja le, a belső célok lehívásait a Server-Side Request Forgery megelőzése PHP-ban.

Hogyan segít konkrétan a PHP SOS?

  • Megkeressük az összes helyet, ahol az alkalmazása és a könyvtárai idegen forrásból származó XML-t dolgoznak fel.
  • Eltávolítjuk a kockázatos értelmező-opciókat, és központi, biztosított betöltő függvényt építünk be.
  • Ellenőrizzük a libxml- és PHP-verziókat az összes szerveren, és régi rendszereknél megtervezzük a migrációt.
  • Ártalmatlan próbadokumentumokkal teszteljük a módosításokat, és dokumentáljuk az eredményt.

A saját szoftver ellenőrzését a biztonsági audit és penetrációs teszt szolgáltatás írja le.

Gyakori kérdések

A PHP 8 óta automatikusan védettek a PHP-alkalmazások az XXE ellen?

Alapesetben nagyrészt igen, mert a libxml a 2.9.0-s verziótól már nem helyettesíti az entityket. A védelem azonban megszűnik, amint a kód olyan opciókat állít be, mint a LIBXML_NOENT, a LIBXML_DTDLOAD vagy a LIBXML_DTDVALID, vagy ha a PHP-t nagyon régi libxml-verzióval fordították.

Továbbra is meg kell hívnom a libxml_disable_entity_loader függvényt?

Nem. A függvény a PHP 8.0 óta elavult, mert a külső entityk betöltése alapértelmezés szerint ki van kapcsolva. További védelemhez a PHP kézikönyve a libxml_set_external_entity_loader függvényt, a PHP 8.4-től pedig megfelelő libxml mellett a LIBXML_NO_XXE konstanst ajánlja.

Hol dolgoz fel az alkalmazásom egyáltalán XML-t?

Gyakran váratlan helyeken: SOAP-interfészek, SAML-lel történő single sign-on, RSS-feedek, sitemapek, termékimportok, SVG-feltöltések és olyan Office-fájlok, amelyek belül XML-ből állnak. A könyvtárak is feldolgozhatnak XML-t anélkül, hogy az a saját kódban látható lenne.

Mi a Billion-Laughs támadás?

Egymásba ágyazott entityket tartalmazó dokumentum, amely feloldáskor óriásira nő, és memóriát meg számítási időt emészt fel. Ugyanabba a családba tartozik, mint az XXE. Védelmet az entity-helyettesítés kikapcsolása és a saját DTD-t tartalmazó dokumentumok elutasítása nyújt.

Források

Azonnali kapcsolat

Minden óra számít. Írjon nekünk most.

Ismertesse röviden a rendszert, a PHP-verziót és a megfigyelését. Jelentkezünk, tisztázzuk a helyzetet, és távolról dolgozunk, amíg az incidens le nem zárul és dokumentálva nem lesz.

Azonnali segítség