Aktuálna situácia: PHP2Shell. Dve kritické zraniteľnosti PHP (CVSS 9.2) umožňujú spustenie škodlivého kódu bez prihlásenia. Záplaty od 21. 10. 2026.Čo treba urobiť teraz →

Ako zabrániť XML External Entities (XXE) v PHP

Ako vývojári bezpečne spracúvajú XML v PHP: štandardné správanie libxml, riskantné možnosti ako LIBXML_NOENT, ochrana pomocou Entity Loader a LIBXML_NO_XXE a typické skryté vstupy XML.

Stav 5 min čítaniaBezpečný vývoj

Stručne: XXE v PHP zabránite tak, že XML z cudzích zdrojov načítate bez možností LIBXML_NOENT, LIBXML_DTDLOAD a LIBXML_DTDVALID, dokumenty s vlastným DTD odmietnete a nastavíte Entity Loader, ktorý externé zdroje odmieta. Od libxml 2.9.0 PHP štandardne entity nenahrádza, preto je libxml_disable_entity_loader() od PHP 8.0 zastaraná. Od PHP 8.4 s libxml 2.13 je navyše k dispozícii LIBXML_NO_XXE.

Stará zraniteľnosť, ktorá nezmizne

XML External Entities, skrátene XXE, považuje veľa vývojárov za vyriešenú tému. PHP s modernými verziami libxml štandardne vypol načítavanie externých entít a stará ochranná funkcia bola v PHP 8.0 označená ako zastaraná. Napriek tomu sa zraniteľnosti XXE v kontrolách stále objavujú.

Dôvod je takmer vždy v jednej z troch situácií: niekto nastavil možnosť na vyriešenie problému s importom a tým opäť zapol nahrádzanie entít. Knižnica to robí interne. Alebo aplikácia beží na starom systéme, ktorého PHP bolo zostavené s veľmi starou libxml.

XXE je nebezpečné, pretože parser XML pritom dosťahuje obsah, na ktorý sa v dokumente len odkazuje: lokálne súbory alebo adresy v sieti. Možné následky sú čítanie konfiguračných súborov, požiadavky na interné systémy podobne ako pri SSRF a preťaženie servera.

Čo platí od libxml 2.9 a PHP 8.0

Príručka PHP opisuje situáciu takto: od libxml 2.9.0 je nahrádzanie entít štandardne vypnuté. Externé entity sa preto už nemusia osobitne vypínať, pokiaľ sa interné odkazy na entity nemajú rozlišovať pomocou určitých možností. Z toho vyplýva tento prehľad:

Možnosť Účinok Riziko pri cudzom XML
LIBXML_NOENT nahrádza entity podľa príručky môže útoky XXE uľahčiť
LIBXML_DTDLOAD načítava externú podmnožinu aktivuje načítavanie externých entít
LIBXML_DTDVALID validuje voči DTD môže útoky XXE uľahčiť
LIBXML_NONET zakazuje sieťové prístupy pri načítavaní ochranná prídavná možnosť
LIBXML_NO_XXE vypína externé entity pri nahrádzaní ochranná, od PHP 8.4 a libxml 2.13.0
LIBXML_PARSEHUGE ruší interné hranice parsera uľahčuje preťaženie, len pre dôveryhodné údaje

Zavádzajúci názov LIBXML_NOENT je známa pasca: znie ako „žiadne entity“, no spôsobuje opak.

Pre vývojárov to znamená: riskantné nie je samotné načítanie XML, ale každé miesto, ktoré štandardné správanie vedome mení. Hľadanie týchto konštánt v kóde je preto najrýchlejšou cestou k prvému odhadu, ešte pred akoukoľvek náročnou kontrolou.

Bezpečná obrana proti XXE v PHP: vzor pre DOMDocument

Nasledujúci príklad kombinuje viacero ochranných vrstiev. Nastaví Entity Loader, ktorý odmieta každý externý zdroj, načíta bez riskantných možností a odmietne dokumenty s vlastným DTD:

<?php
declare(strict_types=1);

function ladeFremdesXml(string $xml): DOMDocument
{
    // Externé zdroje zásadne odmietať
    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;
}

Podľa príručky PHP blokuje návratová hodnota null v loaderi načítanie externého zdroja. OWASP upozorňuje, že nestačí odovzdať null priamo funkcii libxml_set_external_entity_loader(); potrebuje sa funkcia, ktorá vracia null. Pre SimpleXML a XMLReader platia rovnaké možnosti. Kdekoľvek je to možné, OWASP odporúča DTD odmietať úplne.

Tip

Ak môžete dátový formát určiť sami, je JSON pre výmenu medzi vlastnými systémami spravidla jednoduchšou voľbou. Pre cudzie formáty ako SAML alebo súbory Office je XML nevyhnutné.

Nájdenie skrytých vstupov XML

Mnohé aplikácie spracúvajú viac XML, než si ich vývojári myslia. Prejdite si tento zoznam:

  1. Rozhrania: klienti a servery SOAP, XML-RPC, platobné a prepravné služby s odpoveďami XML.
  2. Prihlásenie: jednotné prihlásenie cez SAML sa zakladá na dokumentoch XML, ktoré prichádzajú zvonka.
  3. Importy: údaje o produktoch, kanály, sitemapy, katalógové formáty.
  4. Uploady: obrázky SVG sú XML; formáty Office ako DOCX a XLSX interne pozostávajú zo súborov XML.
  5. Knižnice: balíky Composer pre dokumenty, tabuľky alebo SAML, ktoré interne používajú libxml.

V kóde pomôžu dve hľadania:

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

Nálezy vo vendor/ nie sú dôvodom na paniku, ale podnetom skontrolovať verziu a bezpečnostné hlásenia knižnice.

Pri uploadoch SVG je XXE len jedným z viacerých rizík, pretože súbory SVG môžu obsahovať aj skripty. Ak vaša aplikácia formát nutne nepotrebuje, vôbec ho pre uploady nepovoľte. Inak SVG spracúvajte len podľa bezpečného vzoru vyššie a súbory doručujte tak, aby prehliadač obsiahnuté skripty nevykonal.

Dávajte pozor aj na chybové hlásenia. Ak aplikácia vracia hlásenia libxml používateľom nefiltrované, môžu v nich byť časti spracúvaného obsahu. Chyby patria do protokolu, používateľ dostane všeobecné hlásenie; v príklade vyššie zabezpečuje libxml_use_internal_errors(true), aby sa upozornenia nedostali do výstupu. Obmedzte navyše veľkosť prijatých dokumentov XML skôr, než ich parser vôbec uvidí.

Staré systémy: kedy štandardná ochrana chýba

Ochrana štandardným správaním závisí od verzie libxml, s ktorou bolo PHP zostavené. Ktorá verzia beží, ukáže php -r 'echo LIBXML_DOTTED_VERSION, PHP_EOL;' na príkazovom riadku alebo výstup phpinfo() v chránenom prostredí. Hodnotu skontrolujte pre každú verziu PHP na serveri, pretože CLI a webserver môžu používať odlišné zostavenia. Obrazy kontajnerov prinášajú vlastnú libxml; tam kontrolujte v bežiacom kontajneri, nie na hostiteľovi.

Obzvlášť postihnuté sú systémy, ktoré tak či tak bežia na starých verziách PHP. Pre PHP 8.1 a staršie už po PHP2Shell neexistujú oficiálne aktualizácie projektu PHP; čo to znamená, opisuje PHP 8.1 a staršie: čo platí teraz. Starý systém so zastaranou libxml a otvorenými zraniteľnosťami interpretera by sa nemal len zabezpečiť v kóde, ale migrovať. Príbuzné riziká pri spracovaní cudzích údajov opisuje Nebezpečná deserializácia v PHP, sťahovanie interných cieľov Ako zabrániť Server-Side Request Forgery v PHP.

Ako konkrétne pomáha PHP SOS?

  • Nájdeme všetky miesta, kde vaša aplikácia a jej knižnice spracúvajú XML z cudzích zdrojov.
  • Odstránime riskantné možnosti parsera a zabudujeme centrálnu, zabezpečenú funkciu načítania.
  • Skontrolujeme verzie libxml a PHP na všetkých serveroch a pri starých systémoch naplánujeme migráciu.
  • Zmeny otestujeme neškodnými kontrolnými dokumentmi a výsledok zdokumentujeme.

Kontrolu vlastného softvéru opisuje služba Bezpečnostný audit a penetračný test.

Časté otázky

Sú aplikácie PHP od PHP 8 automaticky chránené pred XXE?

V štandardnom prípade vo veľkej miere, pretože libxml od verzie 2.9.0 už entity nenahrádza. Ochrana však zaniká, hneď ako kód nastaví možnosti ako LIBXML_NOENT, LIBXML_DTDLOAD alebo LIBXML_DTDVALID, alebo ak je PHP zostavené s veľmi starou verziou libxml.

Mám ešte volať libxml_disable_entity_loader?

Nie. Funkcia je od PHP 8.0 zastaraná, pretože načítavanie externých entít je štandardne vypnuté. Pre dodatočnú ochranu odporúča príručka PHP libxml_set_external_entity_loader a od PHP 8.4 s vhodnou libxml konštantu LIBXML_NO_XXE.

Kde moja aplikácia vôbec spracúva XML?

Často na nečakaných miestach: rozhrania SOAP, jednotné prihlásenie (single sign-on) cez SAML, kanály RSS, sitemapy, importy produktov, uploady SVG a súbory Office, ktoré interne pozostávajú z XML. XML môžu spracúvať aj knižnice, bez toho, aby to bolo vidieť vo vlastnom kóde.

Čo je útok Billion Laughs?

Dokument s vnorenými entitami, ktorý sa pri rozbaľovaní enormne zväčšuje a spotrebúva pamäť a výpočtový čas. Patrí do tej istej rodiny ako XXE. Ochranu poskytuje vypnutie nahrádzania entít a odmietanie dokumentov s vlastným DTD.

Zdroje

Okamžitý kontakt

Každá hodina sa počíta. Napíšte nám hneď.

Stručne opíšte systém, verziu PHP a svoje pozorovanie. Ozveme sa, vyjasníme situáciu a budeme pracovať na diaľku, kým nebude incident uzavretý a zdokumentovaný.

Okamžitá pomoc