Trenutno stanje: PHP2Shell. Dve kritični ranljivosti PHP (CVSS 9,2) omogočata izvedbo škodljive kode brez prijave. Popravki od 21. 10. 2026.Kaj je zdaj treba storiti →

Preprečevanje XML External Entities (XXE) v PHP

Kako razvijalci v PHP varno obdelujejo XML: privzeto vedenje libxml, tvegane možnosti, kot je LIBXML_NOENT, zaščita z nalagalnikom entitet in LIBXML_NO_XXE ter tipični skriti vhodi XML.

Stanje 5 min branjaVaren razvoj

Na kratko: XXE v PHP preprečite tako, da XML iz tujih virov nalagate brez možnosti LIBXML_NOENT, LIBXML_DTDLOAD in LIBXML_DTDVALID, zavračate dokumente z lastnim DTD in nastavite nalagalnik entitet, ki zunanje vire zavrne. Od libxml 2.9.0 PHP entitet privzeto ne nadomešča več, zato je libxml_disable_entity_loader() od PHP 8.0 zastarel. Od PHP 8.4 z libxml 2.13 je dodatno na voljo LIBXML_NO_XXE.

Stara ranljivost, ki ne izgine

XML External Entities, na kratko XXE, mnogi razvijalci imajo za rešeno temo. PHP je nalaganje zunanjih entitet pri sodobnih različicah libxml privzeto izklopil, stara zaščitna funkcija pa je bila s PHP 8.0 označena kot zastarela. Kljub temu se ranljivosti XXE v pregledih še vedno pojavljajo.

Razlog je skoraj vedno ena od treh situacij: nekdo je za rešitev težave z uvozom nastavil možnost in s tem spet vklopil nadomeščanje entitet. Knjižnica to naredi interno. Ali pa aplikacija teče na starem sistemu, katerega PHP je bil zgrajen z zelo staro libxml.

XXE je nevaren, ker razčlenjevalnik XML pri tem naloži vsebine, ki so v dokumentu samo omenjene: lokalne datoteke ali naslove v omrežju. Mogoče posledice so branje konfiguracijskih datotek, zahteve k notranjim sistemom podobno kot pri SSRF in preobremenitev strežnika.

Kaj velja od libxml 2.9 in PHP 8.0

Priročnik PHP stanje opisuje takole: od libxml 2.9.0 je nadomeščanje entitet privzeto izklopljeno. Zunanjih entitet zato ni več treba posebej izklapljati, razen če naj se notranje reference entitet razrešijo z določenimi možnostmi. Iz tega sledi ta pregled:

Možnost Učinek Tveganje pri tujem XML
LIBXML_NOENT nadomešča entitete po priročniku lahko olajša napade XXE
LIBXML_DTDLOAD naloži zunanji podnabor omogoči nalaganje zunanjih entitet
LIBXML_DTDVALID preverja glede na DTD lahko olajša napade XXE
LIBXML_NONET prepove omrežne dostope pri nalaganju zaščitna dodatna možnost
LIBXML_NO_XXE pri nadomeščanju izklopi zunanje entitete zaščitna, od PHP 8.4 in libxml 2.13.0
LIBXML_PARSEHUGE odpravi notranje omejitve razčlenjevalnika olajša preobremenitev, samo za zaupanja vredne podatke

Zavajajoče ime LIBXML_NOENT je znana past: zveni kot „brez entitet“, a povzroči nasprotno.

Za razvijalce to pomeni: tvegano ni nalaganje XML kot tako, ampak vsako mesto, ki privzeto vedenje zavestno spremeni. Iskanje teh konstant v kodi je zato najhitrejša pot do prve ocene, še pred vsakim zahtevnim pregledom.

Varna obramba pred XXE v PHP: vzorec za DOMDocument

Naslednji primer združuje več zaščitnih plasti. Nastavi nalagalnik entitet, ki zavrne vsak zunanji vir, nalaga brez tveganih možnosti in zavrača dokumente z lastnim DTD:

<?php
declare(strict_types=1);

function ladeFremdesXml(string $xml): DOMDocument
{
    // Zunanje vire načeloma zavrnite
    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;
}

Po priročniku PHP vrnjena vrednost null v nalagalniku blokira nalaganje zunanjega vira. OWASP opozarja, da ne zadošča neposredno posredovati null v libxml_set_external_entity_loader(); potrebna je funkcija, ki vrne null. Za SimpleXML in XMLReader veljajo enake možnosti. Kjer je le mogoče, OWASP priporoča, da se DTD-ji zavrnejo v celoti.

Nasvet

Če lahko obliko podatkov določite sami, je JSON za izmenjavo med lastnimi sistemi običajno preprostejša izbira. Za tuje oblike, kot sta SAML ali pisarniške datoteke, je XML neizogiben.

Iskanje skritih vhodov XML

Mnoge aplikacije obdelujejo več XML, kot si mislijo njihovi razvijalci. Preglejte ta seznam:

  1. Vmesniki: odjemalci in strežniki SOAP, XML-RPC, ponudniki plačil in dostave z odgovori XML.
  2. Prijava: enotna prijava s SAML temelji na dokumentih XML, ki prihajajo od zunaj.
  3. Uvozi: podatki o izdelkih, viri, zemljevidi strani, katalogne oblike.
  4. Nalaganja: slike SVG so XML; pisarniške oblike, kot sta DOCX in XLSX, so znotraj sestavljene iz datotek XML.
  5. Knjižnice: paketi Composer za dokumente, preglednice ali SAML, ki interno uporabljajo libxml.

V kodi pomagata dve iskanji:

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

Najdbe v vendor/ niso razlog za paniko, so pa povod, da preverite različico in varnostna obvestila knjižnice.

Pri nalaganjih SVG je XXE le eno od več tveganj, saj lahko datoteke SVG vsebujejo tudi skripte. Če vaša aplikacija te oblike nujno ne potrebuje, je za nalaganja sploh ne dovolite. Sicer SVG obdelujte samo z zgornjim varnim vzorcem in datoteke dostavljajte tako, da brskalnik vsebovanih skript ne izvede.

Pazite tudi na sporočila o napakah. Če aplikacija sporočila libxml nefiltrirano vrača uporabnikom, so v njih lahko deli obdelane vsebine. Napake sodijo v dnevnik, uporabnik prejme splošno sporočilo; v zgornjem primeru libxml_use_internal_errors(true) poskrbi, da opozorila ne pridejo v izpis. Poleg tega omejite velikost prejetih dokumentov XML, preden jih razčlenjevalnik sploh vidi.

Stari sistemi: kdaj privzete zaščite ni

Zaščita s privzetim vedenjem je odvisna od različice libxml, s katero je bil PHP zgrajen. Katera različica teče, pokaže php -r 'echo LIBXML_DOTTED_VERSION, PHP_EOL;' v ukazni vrstici ali izpis phpinfo() v zaščitenem okolju. Vrednost preverite za vsako različico PHP na strežniku, ker lahko CLI in spletni strežnik uporabljata različne gradnje. Slike vsebnikov prinesejo lastno libxml; tam preverite v tekočem vsebniku, ne na gostitelju.

Posebej so prizadeti sistemi, ki že tako tečejo na starih različicah PHP. Za PHP 8.1 in starejše po PHP2Shell projekt PHP ne izdaja več uradnih posodobitev; kaj to pomeni, opisuje PHP 8.1 in starejši: kaj velja zdaj. Starega sistema z zastarelo libxml in odprtimi ranljivostmi interpreterja ne bi bilo treba samo zavarovati v kodi, ampak ga migrirati. Sorodna tveganja pri obdelavi tujih podatkov opisuje Nevarna deserializacija v PHP, pridobivanje notranjih ciljev pa Preprečevanje Server-Side Request Forgery v PHP.

Kako konkretno pomaga PHP SOS?

  • Poiščemo vsa mesta, kjer vaša aplikacija in njene knjižnice obdelujejo XML iz tujih virov.
  • Odstranimo tvegane možnosti razčlenjevalnika in vgradimo osrednjo, zavarovano funkcijo nalaganja.
  • Preverimo različice libxml in PHP na vseh strežnikih ter pri starih sistemih načrtujemo migracijo.
  • Spremembe preizkusimo z neškodljivimi testnimi dokumenti in dokumentiramo rezultat.

Pregled lastne programske opreme opisuje storitev Varnostni pregled in penetracijski test.

Pogosta vprašanja

Ali so aplikacije PHP od PHP 8 samodejno zaščitene pred XXE?

V običajnem primeru v veliki meri, ker libxml od različice 2.9.0 entitet ne nadomešča več. Zaščita pa odpade, brž ko koda nastavi možnosti, kot so LIBXML_NOENT, LIBXML_DTDLOAD ali LIBXML_DTDVALID, ali je PHP zgrajen z zelo staro različico libxml.

Ali naj libxml_disable_entity_loader še kličem?

Ne. Funkcija je od PHP 8.0 zastarela, ker je nalaganje zunanjih entitet privzeto izklopljeno. Za dodatno zaščito priročnik PHP priporoča libxml_set_external_entity_loader, od PHP 8.4 z ustrezno različico libxml pa konstanto LIBXML_NO_XXE.

Kje moja aplikacija sploh obdeluje XML?

Pogosto na nepričakovanih mestih: vmesniki SOAP, enotna prijava s SAML, viri RSS, zemljevidi strani, uvozi izdelkov, nalaganja SVG in pisarniške datoteke, ki so znotraj sestavljene iz XML. Tudi knjižnice lahko obdelujejo XML, ne da bi bilo to vidno v lastni kodi.

Kaj je napad Billion Laughs?

Dokument z gnezdenimi entitetami, ki se ob razreševanju silno poveča in porabi pomnilnik ter računski čas. Spada v isto družino kot XXE. Zaščito nudita izklop nadomeščanja entitet in zavračanje dokumentov z lastnim DTD.

Viri

Takojšnji stik

Vsaka ura šteje. Pišite nam zdaj.

Na kratko opišite sistem, različico PHP in opažanje. Odzovemo se, razjasnimo stanje in delamo na daljavo, dokler incident ni zaključen in dokumentiran.

Takojšnja pomoč