Lyhyesti: XXE:n estätte PHP:ssä lataamalla vierasta XML:ää ilman asetuksia LIBXML_NOENT, LIBXML_DTDLOAD ja LIBXML_DTDVALID, hylkäämällä omaa DTD:tä sisältävät dokumentit ja asettamalla entity-loaderin, joka kieltää ulkoiset resurssit. Libxml 2.9.0:sta alkaen PHP ei enää oletuksena korvaa entityjä, minkä vuoksi libxml_disable_entity_loader() on vanhentunut PHP 8.0:sta alkaen. PHP 8.4:stä alkaen libxml 2.13:lla on lisäksi käytettävissä LIBXML_NO_XXE.
Vanha aukko, joka ei katoa
XML External Entities, lyhyesti XXE, katsotaan monen kehittäjän mielestä jo hoidetuksi aiheeksi. PHP on kytkenyt ulkoisten entityjen latauksen oletuksena pois nykyisillä libxml-versioilla, ja vanha suojafunktio merkittiin vanhentuneeksi PHP 8.0:ssa. Silti XXE-aukkoja löytyy tarkistuksissa edelleen.
Syy on lähes aina yksi kolmesta tilanteesta: joku on asettanut asetuksen ratkaistakseen tuontiongelman ja kytkenyt siten entityjen korvauksen takaisin päälle. Kirjasto tekee niin sisäisesti. Tai sovellus toimii vanhassa järjestelmässä, jonka PHP on rakennettu hyvin vanhalla libxmlillä.
XXE on vaarallinen, koska XML-jäsennin lataa silloin sisältöä, johon dokumentissa vain viitataan: paikallisia tiedostoja tai osoitteita verkossa. Mahdollisia seurauksia ovat asetustiedostojen lukeminen, pyynnöt sisäisiin järjestelmiin SSRF:n tapaan ja palvelimen ylikuormitus.
Mikä pätee libxml 2.9:stä ja PHP 8.0:sta alkaen
PHP-käsikirja kuvaa tilanteen näin: libxml 2.9.0:sta alkaen entityjen korvaus on oletuksena pois päältä. Ulkoisia entityjä ei siksi tarvitse erikseen kytkeä pois, ellei sisäisiä entity-viittauksia haluta ratkaista tietyillä asetuksilla. Siitä saadaan tämä yleiskatsaus:
| Asetus | Vaikutus | Riski vieraalla XML:llä |
|---|---|---|
LIBXML_NOENT |
korvaa entityt | käsikirjan mukaan voi helpottaa XXE-hyökkäyksiä |
LIBXML_DTDLOAD |
lataa ulkoisen osajoukon | kytkee ulkoisten entityjen latauksen päälle |
LIBXML_DTDVALID |
validoi DTD:tä vasten | voi helpottaa XXE-hyökkäyksiä |
LIBXML_NONET |
kieltää verkkoyhteydet latauksessa | suojaava lisäasetus |
LIBXML_NO_XXE |
kytkee ulkoiset entityt pois korvauksessa | suojaava, PHP 8.4:stä ja libxml 2.13.0:sta alkaen |
LIBXML_PARSEHUGE |
poistaa jäsentimen sisäiset rajat | helpottaa ylikuormitusta, vain luotetuille tiedoille |
LIBXML_NOENT-asetuksen harhaanjohtava nimi on tunnettu ansa: se kuulostaa ”ei entityjä”, mutta saa aikaan päinvastaisen.
Kehittäjälle tämä tarkoittaa: riskialtista ei ole XML:n lataaminen sinänsä vaan jokainen kohta, joka muuttaa oletuskäyttäytymistä tietoisesti. Näiden vakioiden haku koodista on siksi nopein tie ensiarvioon, ennen mitään työlästä tarkistusta.
XXE:n turvallinen torjunta PHP:ssä: malli DOMDocumentille
Seuraava esimerkki yhdistää useita suojakerroksia. Se asettaa entity-loaderin, joka kieltää jokaisen ulkoisen resurssin, lataa ilman riskialttiita asetuksia ja hylkää omaa DTD:tä sisältävät dokumentit:
<?php
declare(strict_types=1);
function ladeFremdesXml(string $xml): DOMDocument
{
// Kiellä ulkoiset resurssit periaatteessa
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äsikirjan mukaan loaderin palautusarvo null estää ulkoisen resurssin latauksen. OWASP huomauttaa, ettei riitä välittää null suoraan funktiolle libxml_set_external_entity_loader(); tarvitaan funktio, joka palauttaa null. SimpleXML:lle ja XMLReaderille pätevät samat asetukset. Missä vain mahdollista, OWASP suosittelee DTD:iden hylkäämistä kokonaan.
Jos voitte itse määrätä tietomuodon, JSON on omien järjestelmien väliseen vaihtoon yleensä yksinkertaisempi valinta. Vieraille muodoille kuten SAML tai toimistotiedostot XML on väistämätön.
Piilotettujen XML-syötteiden löytäminen
Monet sovellukset käsittelevät enemmän XML:ää kuin kehittäjät luulevat. Käykää tämä luettelo läpi:
- Rajapinnat: SOAP-asiakkaat ja -palvelimet, XML-RPC, maksu- ja toimituspalvelut XML-vastauksilla.
- Kirjautuminen: SAML-kertakirjautuminen perustuu XML-dokumentteihin, jotka tulevat ulkopuolelta.
- Tuonnit: tuotetiedot, syötteet, sivukartat, katalogimuodot.
- Uploadit: SVG-kuvat ovat XML:ää; toimistomuodot kuten DOCX ja XLSX koostuvat sisäisesti XML-tiedostoista.
- Kirjastot: Composer-paketit asiakirjoille, taulukoille tai SAML:lle, jotka käyttävät sisäisesti libxmlia.
Koodissa auttaa kaksi hakua:
grep -rnE "LIBXML_(NOENT|DTDLOAD|DTDVALID|PARSEHUGE)" src/ vendor/ --include="*.php"
grep -rnE "(loadXML|simplexml_load_(string|file)|XMLReader|->load\()" src/ --include="*.php"
Löydökset hakemistossa vendor/ eivät ole syy paniikkiin, mutta syy tarkistaa kirjaston versio ja tietoturvailmoitukset.
SVG-uploadeissa XXE on vain yksi useista riskeistä, sillä SVG-tiedostot voivat sisältää myös skriptejä. Jos sovelluksenne ei välttämättä tarvitse muotoa, älkää sallikaa sitä uploadeihin lainkaan. Muuten käsitelkää SVG:tä vain yllä olevalla turvallisella mallilla ja toimittakaa tiedostot niin, ettei selain suorita niiden sisältämiä skriptejä.
Kiinnittäkää huomiota myös virheilmoituksiin. Jos sovellus palauttaa libxmlin ilmoitukset suodattamattomina käyttäjille, niissä voi olla osia käsitellystä sisällöstä. Virheet kuuluvat lokiin, ja käyttäjä saa yleisen ilmoituksen; yllä olevassa esimerkissä libxml_use_internal_errors(true) huolehtii siitä, etteivät varoitukset päädy tulosteeseen. Rajatkaa lisäksi vastaanotettujen XML-dokumenttien koko, ennen kuin jäsennin ylipäätään näkee ne.
Vanhat järjestelmät: milloin oletussuoja puuttuu
Oletuskäyttäytymisen tarjoama suoja riippuu libxml-versiosta, jolla PHP on rakennettu. Mikä versio on käytössä, näyttää php -r 'echo LIBXML_DOTTED_VERSION, PHP_EOL;' komentoriviltä tai phpinfo()-tuloste suojatussa ympäristössä. Tarkistakaa arvo jokaiselle palvelimen PHP-versiolle, sillä CLI ja verkkopalvelin voivat käyttää eri buildeja. Konttikuvat tuovat mukanaan oman libxmlinsä; tarkistakaa se käynnissä olevasta kontista, älkää isännältä.
Erityisesti ovat kohteena järjestelmät, jotka toimivat joka tapauksessa vanhoilla PHP-versioilla. PHP 8.1:lle ja sitä vanhemmille ei PHP2Shellin jälkeen ole enää PHP-projektin virallisia päivityksiä; mitä se tarkoittaa, kuvaa PHP 8.1 ja vanhemmat: mikä nyt pätee. Vanha järjestelmä, jossa on vanhentunut libxml ja avoimia tulkin aukkoja, tulisi paitsi suojata koodissa myös migroida. Vastaavia riskejä vieraiden tietojen käsittelyssä kuvaa Turvaton deserialisointi PHP:ssä, sisäisten kohteiden hakuja Server-Side Request Forgeryn estäminen PHP:ssä.
Miten PHP SOS auttaa konkreettisesti?
- Löydämme kaikki kohdat, joissa sovelluksenne ja sen kirjastot käsittelevät XML:ää vieraista lähteistä.
- Poistamme riskialttiit jäsennysasetukset ja rakennamme keskitetyn, suojatun latausfunktion.
- Tarkistamme libxml- ja PHP-versiot kaikilla palvelimilla ja suunnittelemme vanhojen järjestelmien migraation.
- Testaamme muutokset harmittomilla testidokumenteilla ja dokumentoimme tuloksen.
Oman ohjelmiston tarkistuksen kuvaa palvelu tietoturva-auditointi ja tunkeutumistesti.
Usein kysytyt kysymykset
Ovatko PHP-sovellukset PHP 8:sta alkaen automaattisesti suojattuja XXE:ltä?
Vakiotapauksessa pitkälti, koska libxml ei enää korvaa entityjä versiosta 2.9.0 alkaen. Suoja kuitenkin poistuu heti, kun koodi asettaa asetuksia kuten LIBXML_NOENT, LIBXML_DTDLOAD tai LIBXML_DTDVALID tai PHP on rakennettu hyvin vanhalla libxml-versiolla.
Pitäisikö minun yhä kutsua libxml_disable_entity_loader?
Ei. Funktio on vanhentunut PHP 8.0:sta alkaen, koska ulkoisten entityjen lataus on oletuksena pois päältä. Lisäsuojaksi PHP-käsikirja suosittelee funktiota libxml_set_external_entity_loader ja PHP 8.4:stä alkaen sopivalla libxmlillä vakiota LIBXML_NO_XXE.
Missä sovellukseni ylipäätään käsittelee XML:ää?
Usein odottamattomissa paikoissa: SOAP-rajapinnat, kertakirjautuminen SAML:lla, RSS-syötteet, sivukartat, tuotetuonnit, SVG-uploadit ja toimisto-tiedostot, jotka koostuvat sisäisesti XML:stä. Myös kirjastot voivat käsitellä XML:ää niin, ettei se näy omassa koodissa.
Mikä on Billion-Laughs-hyökkäys?
Dokumentti sisäkkäisillä entityillä, joka kasvaa purettaessa valtavasti ja kuluttaa muistia ja laskenta-aikaa. Se kuuluu samaan perheeseen kuin XXE. Suojaa tarjoavat entityjen korvauksen kytkeminen pois ja omaa DTD:tä sisältävien dokumenttien hylkääminen.