Bölüm 10
XML External Entity (XXE)
Bölüm 1 (güven sınırı), Bölüm 4 (girdi doğrulama), Bölüm 21'e köprü (SSRF).
XXE'yi, XML'in tarihsel bir özelliğinin (harici varlıklar) kötüye kullanımı olarak kavrayacak; PHP'de bu açığın neden büyük ölçüde yapılandırma meselesi olduğunu, libxml sürüm geçmişini ve modern PHP'de bile insanı yakalayan LIBXMLNOENT tuzağını göreceksiniz. XXE'nin yalnızca dosya okuma değil, SSRF ve DoS'a da açıldığını inceleyeceğiz.
1. Giriş
XXE, güvenlik açıklarının en "sinsi" sınıflarından biridir çünkü kökü, kötü niyetli bir tasarımda değil, XML'in meşru ama tehlikeli bir özelliğinde yatar: harici varlıklar (external entities). XML 1.0 spesifikasyonu 1998'de yayımlandığında, belgelerin dışarıdan içerik (bir dosya, bir URL) çekmesine izin veren bir mekanizma içeriyordu. O gün için makul görülen bu özellik, XML'in web servislerinde (SOAP, SAML, RSS, Office belgeleri, dosya meta verileri) yaygınlaşmasıyla devasa bir saldırı yüzeyine dönüştü.
Sorunun büyüklüğü, XML ayrıştırıcılarının tarihsel olarak bu özelliği varsayılan açık getirmesinden kaynaklandı. Java, .NET, Python ve PHP'nin altında yatan libxml — hepsi yıllarca harici varlık işlemeyi öntanımlı etkin tuttu. Böylece bir geliştirici, "sadece XML ayrıştırıyorum" diye masum bir kod yazarken, farkında olmadan bir dosya okuma ve SSRF vektörü açıyordu.
PHP özelinde bu bölümün ana teması şudur: XXE, çoğunlukla bir kod hatası değil, bir yapılandırma/sürüm hatasıdır. Modern PHP (8.0+) ve libxml (2.9+) güvenli varsayılanlarla gelir; ama eski dağıtımlar, yanlış bayraklar (LIBXML_NOENT) ve şaşırtıcı geriye-uyumluluk davranışları, açığı hâlâ canlı tutar.
2. Temel Teori
XXE'yi anlamak için önce XML'in üç yapı taşını netleştirelim.
DTD (Document Type Definition) ve DOCTYPE. Bir XML belgesi, başında bir <!DOCTYPE ...> bloğuyla kendi yapısını tanımlayabilir. Bu blok, "varlık (entity)" tanımlarını içerebilir.
Varlık (entity) türleri:
- Dahili varlık: Belge içinde tanımlı bir kısaltma. <!ENTITY sirket "Acme A.Ş."> → &sirket; metne "Acme A.Ş." koyar. Zararsız.
- Harici genel varlık: Değeri dışarıdan gelen varlık. <!ENTITY xxe SYSTEM "file:///etc/passwd"> → &xxe;, ayrıştırıcının o dosyayı okuyup içeriğini belgeye gömmesine yol açar. XXE'nin kalbi budur.
- Harici parametre varlığı: DTD içinde kullanılan, % ile gösterilen varlık. Kör (blind) ve bant-dışı (out-of-band) XXE saldırılarının temelini oluşturur.
Kök neden. XXE, tanıdık kalıbın bir çeşididir: güvenilmeyen XML, harici varlıkları çözen bir ayrıştırıcıya verilir ve ayrıştırıcı, saldırganın belirttiği kaynağı (dosya/URL) okuyup sonucu belgeye gömer. Fark, buradaki "yorumlayıcının" bir SQL motoru veya kabuk değil, XML ayrıştırıcısının varlık çözümleyicisi olmasıdır.
Neyi mümkün kılar? Harici varlık bir kaynağı işaret ettiği için, saldırgan o kaynağı seçebildiğinde üç sınıf etki doğar:
| Şema | Etki | Örnek |
|---|---|---|
file:// |
Yerel dosya okuma | /etc/passwd, uygulama kaynağı, sırlar |
http:// / https:// |
SSRF (Bölüm 21) | İç servisler, bulut metadata (169.254.169.254) |
| İç içe varlık | DoS | "Billion laughs" (üstel varlık genişlemesi) |
php://filter/convert.base64-encode/... gibi PHP akış sarmalayıcıları, ikili/özel karakter içeren dosyaların XML'i bozmadan (base64 olarak) okunmasına imkân verdiği için PHP'de dosya okumayı özellikle güçlü kılar.
PHP'ye özgü nokta — libxml geçmişi. PHP'nin XML ayrıştırıcıları (DOMDocument, SimpleXML, XMLReader) libxml'e dayanır. libxml 2.9.0 (2012) ile harici varlık yüklemesi öntanımlı devre dışı hâle geldi. Bu yüzden tarihsel savunma olan libxml_disable_entity_loader(true), PHP 8.0'da kullanımdan kaldırıldı (deprecated) — çünkü modern libxml zaten güvenli. Ancak bu, iki tuzak yarattı: (1) eski dağıtımlarda hâlâ savunmasız libxml olabilir; (2) LIBXML_NOENT bayrağı, güvenli varsayılanı geri çevirir (Bölüm 5'teki WordPress vakası).
3. Mimarisel Bakış
GÜVENSİZ (harici varlık çözülüyor):
Saldırgan XML → <!DOCTYPE ... <!ENTITY xxe SYSTEM "file:///etc/passwd">>
↓ ╳ güven sınırı
PHP: DOMDocument->loadXML($xml, LIBXML_NOENT) ← NOENT varlıkları ÇÖZER
↓
libxml varlık çözümleyici → file:///etc/passwd OKUNUR
↓
İçerik belgeye gömülür → yanıtta saldırgana döner (dosya ifşası)
Eğer varlık http://ic-servis → SSRF (Bölüm 21)
Eğer iç içe varlık → billion laughs → DoS
GÜVENLİ (varlık çözümü kapalı):
Saldırgan XML → <!DOCTYPE ...>
↓
PHP: DOMDocument->loadXML($xml, LIBXML_NONET) ← ağ yok, NOENT yok
↓ (modern libxml: harici varlık öntanımlı kapalı)
Harici varlık çözülmez → &xxe; ya boş ya hata → ifşa yok
Kritik gözlem: güven sınırı ayrıştırıcının yapılandırmasındadır. Aynı DOMDocument, bayraklara göre güvenli veya savunmasız olur. XXE savunması bu yüzden büyük ölçüde "doğru bayrak / doğru sürüm" meselesidir.
4. Güvensiz Kod
Gerçekçi bir "XML tabanlı API / dosya içe aktarma" uç noktası:
<?php
// ⚠️ GÜVENSİZ — harici varlık çözümü etkin
declare(strict_types=1);
$xml = file_get_contents('php://input'); // saldırgan kontrollü XML gövdesi
// Tuzak: LIBXML_NOENT varlık SUBSTİTÜSYONUNU AÇAR (adı yanıltıcı!)
$dom = new DOMDocument();
$dom->loadXML($xml, LIBXML_NOENT | LIBXML_DTDLOAD);
// XML'den bir alanı oku ve yanıtla
$name = $dom->getElementsByTagName('name')->item(0)->textContent ?? '';
echo "Merhaba " . htmlspecialchars($name, ENT_QUOTES, 'UTF-8');
Ve SimpleXML ile eski-tarz bir varyant:
<?php
// ⚠️ GÜVENSİZ — eski PHP/libxml varsayımıyla
libxml_disable_entity_loader(false); // savunmayı KAPATIYOR (PHP 8'de deprecated)
$data = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOENT);
5. Açığın Analizi
loadXML($xml, LIBXML_NOENT | LIBXML_DTDLOAD) — İşte XXE'nin en öğretici tuzağı. LIBXML_NOENT bayrağının adı yanıltıcıdır: "no entity" gibi okunur ama anlamı "belgede varlık bırakma, hepsini çöz/değiştir"dir. Yani bu bayrak, harici varlık dahil tüm varlıkları substitüe eder — güvenli varsayılanı bilinçli olarak devre dışı bırakır. LIBXML_DTDLOAD ise DTD'nin (ve içindeki harici tanımların) yüklenmesini açar. İkisi birlikte, modern libxml'de bile XXE'yi yeniden mümkün kılar. Saldırgan gövdeye bir <!DOCTYPE> + harici varlık koyduğunda, $dom o dosyayı/URL'i çözer ve textContent ile yanıta sızdırılır.
libxml_disable_entity_loader(false) — Savunmayı açıkça kapatan bir çağrı. PHP 8'de bu fonksiyon deprecated'tir (çünkü artık gereksiz), ama false vermek eski davranışı zorlayabilir ve niyeti tersine çevirir. Kod incelemede bu çağrının false argümanı bir kırmızı bayraktır.
Ortak kök: İki durumda da ayrıştırıcı, güvenilmeyen XML içindeki harici varlıkları çözecek biçimde yapılandırılmış. Sorun XML verisinde değil, ayrıştırıcının çözme kararındadır. Bu yüzden XXE'ye "yapılandırma açığı" da denir.
6. Hacker Bakış Açısı
Saldırgan XXE'yi nasıl keşfeder?
XML kabul eden yerleri arar. XXE yalnızca "bariz" XML uç noktalarında değildir. SOAP/REST API'leri, SAML kimlik doğrulama akışları, RSS/Atom işleyiciler, SVG/Office/docx yükleme (bunlar zip içinde XML'dir), dosya meta verisi ayrıştırma (Bölüm 11'deki WordPress getID3 gibi) — hepsi potansiyel giriş. Saldırgan, Content-Type: application/xml kabul eden veya XML tabanlı bir dosya türü işleyen her yeri sınar.
Zararsız bir varlık sondası gönderir. Önce dahili bir varlık tanımlayıp yanıtta çözülüp çözülmediğine bakar. Çözülüyorsa, ayrıştırıcı varlıkları işliyor demektir — harici varlığa geçmenin yolu açıktır.
Görünür yanıt yoksa körlemesine gider. Uygulama XML içeriğini geri yansıtmıyorsa (blind XXE), saldırgan harici parametre varlıkları ve bant-dışı (OOB) teknikler kullanır: XML, saldırganın sunucusuna bir istek atacak biçimde kurulur; isteğin gelip gelmediği (ve içinde ne taşındığı) açığı doğrular ve veriyi sızdırır. Bu, tam olarak time-based/OOB SQLi (Bölüm 6) ve komut enjeksiyonu (Bölüm 8) mantığının XML'deki karşılığıdır.
Etkiyi genişletir. Dosya okuyabiliyorsa, iç ağa istek atmayı (SSRF, Bölüm 21) — özellikle bulut metadata uç noktasını (169.254.169.254) — dener; bu, bulut kimlik bilgilerinin çalınmasına giden yoldur. Ayrıca DoS için üstel varlık genişlemesini (billion laughs) test eder.
Savunmacı dersi: XXE'nin girişi "bariz XML" ile sınırlı değildir; XML içeren her format (belge, imza, meta veri) bir vektördür. Ayrıca görünür yanıt olmaması güvenlik sağlamaz — kör XXE gerçektir.
7. Exploit Mantığı
Bu bölüm çalıştırılabilir sızdırma zincirleri içermez. XXE'nin neden güçlü olduğu kavramsaldır: saldırgan, ayrıştırıcıyı kendi adına bir dosya okuyucu ve HTTP istemcisi gibi kullanır.
- Dosya okuma: Harici varlık bir yerel dosyayı işaret ederse ve içerik yanıtta görünürse, saldırgan sunucu dosya sistemini okur.
php://filterbase64 sarmalayıcısı, XML'i bozan ikili/özel karakterli dosyaları bile okumayı mümkün kılar. - SSRF: Harici varlık bir URL'i işaret ederse, sunucu o isteği yapar. Bu, iç servislere ve bulut metadata'sına erişimin (Bölüm 21) bir yoludur; ayrıştırıcı, saldırganın ağdaki proxy'sine dönüşür.
- Kör/OOB sızdırma: Yanıt görünmese bile, parametre varlıkları ile saldırgan, okunan dosya içeriğini kendi sunucusuna bir istek parametresi olarak gönderten bir DTD kurabilir. Görünürlük gerekmez.
- DoS: İç içe geçmiş varlıklar üstel genişleme (billion laughs) yaratıp belleği tüketebilir; harici varlık tabanlı özyineleme benzer etki verir.
- Nadiren RCE:
expect://gibi sarmalayıcılar etkinse (çok nadir yapılandırma), XXE komut çalıştırmaya bile tırmanabilir.
Araştırmacının çerçevesi: (a) ayrıştırıcı harici varlık çözüyor mu, (b) yanıt görünür mü yoksa kör mü, (c) etki nereye tırmanıyor (dosya / SSRF / DoS / RCE). Savunma, (a)'yı kapatarak zinciri kaynağında keser.
8. Güvenli Kod
Modern PHP'de doğru yaklaşım: harici varlık çözümünü açan bayrakları kullanma; ağı kapat.
<?php
// ✅ GÜVENLİ — modern PHP/libxml, güvenli bayraklar
declare(strict_types=1);
$xml = file_get_contents('php://input');
$dom = new DOMDocument();
// LIBXML_NOENT ve LIBXML_DTDLOAD YOK. LIBXML_NONET ağ erişimini keser.
$ok = $dom->loadXML($xml, LIBXML_NONET);
if ($ok === false) {
http_response_code(400);
exit('Geçersiz XML');
}
$name = $dom->getElementsByTagName('name')->item(0)?->textContent ?? '';
echo 'Merhaba ' . htmlspecialchars($name, ENT_QUOTES, 'UTF-8');
DTD'yi tamamen reddederek daha da katı olmak (girdi hiç DOCTYPE içermemeliyse):
// ✅ Daha katı: DOCTYPE içeren girdiyi baştan reddet
if (preg_match('/<!DOCTYPE/i', $xml)) {
http_response_code(400);
exit('DOCTYPE yasak');
}
$dom->loadXML($xml, LIBXML_NONET);
SimpleXML/XMLReader için de aynı ilke; LIBXML_NOENT asla verilmez:
// ✅ SimpleXML — NOENT YOK, NONET VAR
$sx = simplexml_load_string($xml, SimpleXMLElement::class, LIBXML_NONET);
Öne çıkan modern kalıplar:
- LIBXML_NOENT kullanma: Adı yanıltıcı bu bayrak varlık substitüsyonunu açar; XXE'nin en yaygın nedenidir.
- LIBXML_NONET: Ayrıştırıcının ağa çıkmasını engeller; SSRF ve harici DTD çekmeyi kırar.
- DTD'yi reddet: Uygulama DOCTYPE beklemiyorsa, içeren girdiyi baştan at (en katı savunma).
- Sürümü güncel tut: Modern libxml (2.9+) harici varlığı öntanımlı kapatır; eski dağıtımlar için libxml_disable_entity_loader(true) hâlâ PHP 7.x'te gerekir.
- Mümkünse XML yerine JSON: OWASP'ın tavsiyesi — XML zorunlu değilse daha az ifade gücüne sahip formatları tercih et; XXE yüzeyi tümden kaybolur.
- Şema doğrulama (XSD): XML zorunluysa, beklenen yapıyı bir şemaya bağla ve beklenmeyen DOCTYPE'ları reddet.
9. Patch Analizi
Neden işe yarar? Harici varlık çözümü kapatıldığında, ayrıştırıcı <!ENTITY ... SYSTEM ...> tanımını görse bile o kaynağı okumaz; varlık ya boş kalır ya hata verir. Kaynak hiç okunmadığından ne dosya ifşası ne SSRF ne de OOB sızdırma mümkündür. Bu, "veriyi temizleme" değil, "tehlikeli yeteneği kapatma" yaklaşımıdır ve bu yüzden kapsamlıdır.
Alternatiflerin karşılaştırması:
| Yaklaşım | Kapsam | Not |
|---|---|---|
LIBXML_NOENT kullanmamak |
Varlık substitüsyonunu kapatır | Modern PHP'de temel; en sık hata bunu vermek |
LIBXML_NONET |
Ağı keser | SSRF ve harici DTD'yi engeller |
| DOCTYPE reddi | En katı | Uygulama DTD beklemiyorsa ideal |
libxml_disable_entity_loader(true) |
Tüm harici yüklemeyi kapatır | PHP 7.x'te gerekli; PHP 8'de deprecated |
| JSON'a geçiş | XXE yüzeyini yok eder | Uygulanabildiğinde en temiz çözüm |
Performans: Harici varlık/ağ erişimini kapatmak aslında daha hızlıdır (dosya/ağ I/O'su yok) ve DoS'a karşı koruyucudur (billion laughs engellenir). Güvenli yol yine hızlı yoldur.
10. Gerçek Hayat Senaryosu
Kurgu — bir B2B entegrasyon platformu. Platform, iş ortaklarından SOAP/XML tabanlı sipariş dosyaları alıp işliyor. Sunucu, uzun süredir çalışan eski bir PHP 7.2 dağıtımında ve XML ayrıştırma kodu LIBXML_NOENT kullanıyor (bir geliştirici yıllar önce "varlıkları düzgün çözsün" diye eklemiş). Bir iş ortağı hesabını ele geçiren saldırgan, sipariş XML'ine bir harici varlık gömüyor. Sunucu bulutta çalıştığından, saldırgan önce /etc/passwd ve uygulama .env dosyasını okuyor, sonra harici varlığı bulut metadata uç noktasına (169.254.169.254) yönlendirerek geçici IAM kimlik bilgilerini çalıyor ve bu kimliklerle bulut hesabına yanal hareket ediyor. Dersler: (1) LIBXML_NOENT iyi niyetle eklenmiş ama açığın kaynağı olmuş; (2) XXE, dosya okumadan bulut ele geçirmeye tırmandı (SSRF köprüsü); (3) en az yetki (parser'ın erişemeyeceği sırlar, kısıtlı IAM rolü) hasarı sınırlardı.
11. Gerçek CVE Analizi — WordPress Media XXE (CVE-2021-29447)
Sonar (SonarSource) analizi, WordPress güvenlik notları ve MITRE/NVD ile doğrulanmıştır. Silahlaştırılmış payload verilmez.
Özet. WordPress 5.6–5.7 sürümlerinde, Medya Kütüphanesi üzerinden bir XXE açığı bulundu. Kimlik doğrulamalı (en az "author" yetkili) bir kullanıcı, özel hazırlanmış bir ses dosyası yükleyerek sunucudan dosya okuyabiliyor ve SSRF gerçekleştirebiliyordu. Açık, özellikle PHP 8 çalıştıran kurulumları etkilediği için öğreticidir; PHP'nin sürüm-bağımlı XXE davranışının mükemmel bir örneğidir.
Teknik neden — ve neden PHP'nin XXE tuzağını özetler. WordPress, yüklenen medya dosyalarından meta veri (sanatçı, başlık vb.) çıkarmak için getID3 kütüphanesini kullanır; bu meta verinin bir kısmı XML biçimindedir. İlgili WordPress kodu, tarihsel XXE savunması olan libxml_disable_entity_loader(true) çağrısını içeriyordu — ama bunu if (PHP_VERSION_ID < 80000) koşuluyla sarmıştı; çünkü fonksiyon PHP 8'de deprecated'ti. Mantık şuydu: "PHP 8'de libxml zaten güvenli, savunmaya gerek yok."
İşte ince nokta: XML ayrıştırma çağrısı LIBXML_NOENT bayrağıyla yapılıyordu. Bölüm 5'te vurgulandığı gibi LIBXML_NOENT, adının aksine varlık substitüsyonunu etkinleştirir — yani harici varlıklar çekilip değiştirilir. Sonuç: PHP 8'de savunma çağrısı atlandığı ve LIBXML_NOENT substitüsyonu zorladığı için, daha önce (WordPress 3.9.2'de) kapatılmış olan XXE yeniden açıldı. Bu, iki güvenli-görünen kararın (deprecated fonksiyonu atlamak + "varlıkları düzgün çöz" bayrağı) birleşince açık ürettiği bir örnektir.
Etki. Author yetkisine sahip bir saldırgan (ki bu birçok çok-yazarlı sitede düşük bir eşiktir) yerel dosya okuyabildi ve SSRF gerçekleştirebildi — WordPress'in dünya çapındaki yaygınlığı düşünüldüğünde geniş bir yüzey. Açık, WordPress 5.7.1 ile düzeltildi (ayrıştırma bayrakları ve varlık işleme güvenli hâle getirildi) ve CVE-2021-29447 olarak kaydedildi.
Çıkarılacak dersler:
1. LIBXML_NOENT yanıltıcıdır ve XXE'nin en yaygın nedenidir. Adına aldanıp "varlıkları temizler" sanmayın; substitüsyonu açar.
2. Sürüm-bağımlı güvenlik kodu kırılgandır. "Yeni sürümde güvenli" varsayımı, başka bir bayrak güvensiz varsayılanı geri getirdiğinde çöker. Savunmaları katmanlı ve sürümden bağımsız kurun.
3. XXE yüzeyi "bariz XML" değildir. Burada vektör bir ses dosyasının meta verisiydi; dosya yükleme (Bölüm 19) ve bağımlılık davranışı (Bölüm 30) birlikte düşünülmeliydi.
12. Detection
Kod incelemede: Ayrıştırıcı yapılandırmasına ve tehlikeli bayraklara odaklanın — XXE bir yapılandırma açığı olduğundan bu en güvenilir tespittir:
grep -rn 'LIBXML_NOENT' src/ # neredeyse her zaman risk
grep -rnE 'loadXML|simplexml_load|XMLReader|DOMDocument' src/
grep -rn 'libxml_disable_entity_loader.*false' src/ # savunmayı kapatan
Her bulguda: LIBXML_NOENT/LIBXML_DTDLOAD var mı? LIBXML_NONET verilmiş mi? Girdi güvenilmeyen mi?
SAST: XXE tespitinde SAST güçlüdür çünkü açık tek bir "kirli dize"de değil, parser instantiation + bayrak kombinasyonunda yaşar. Kural: harici varlık koruması olmadan XML ayrıştırıcı örneklenmesini işaretle. Bu, regex-tabanlı girdi taramasından çok daha güvenilirdir.
DAST: XML uç noktalarına dahili/harici varlık sondaları gönderip yanıtı ve OOB geri çağrıları (kendi sunucusuna gelen istek) izleyerek hem görünür hem kör XXE'yi tespit eder.
WAF: <!DOCTYPE, <!ENTITY, SYSTEM, php://filter kalıplarını yakalayabilir; ama kodlama ve alternatif yapılarla atlatılabilir. Ek katman.
Loglar/SIEM: XML işleyen uygulamadan beklenmeyen giden ağ istekleri (özellikle iç IP'lere veya 169.254.169.254'e) en güçlü sinyaldir — çünkü XXE genelde SSRF'e köprüdür. Ayrıca file:///php:// referansları içeren girdiler ve ayrıştırıcı hataları kümesi izlenir.
13. Prevention
LIBXML_NOENTkullanma; harici varlık substitüsyonunu asla açma.LIBXML_NONETver; ayrıştırıcının ağa çıkışını kapat (SSRF + harici DTD engellenir).- DTD/DOCTYPE'ı reddet (uygulama beklemiyorsa) — en katı savunma.
- Sürümü güncel tut: Modern libxml/PHP güvenli varsayılan; PHP 7.x'te
libxml_disable_entity_loader(true). - JSON'ı tercih et: XML zorunlu değilse XXE yüzeyini tümden kaldır (OWASP tavsiyesi).
- Şema doğrula (XSD): Beklenen yapıyı zorla; beklenmeyeni reddet.
- En az yetki: Ayrıştıran süreç, hassas dosyalara ve iç ağa erişemesin; hasarı sınırla.
- Bağımlılıkları dahil et: XML'i sizin adınıza ayrıştıran kütüphaneleri (getID3, SAML, SOAP) de denetle (Bölüm 30).
14. Mitigation
- Acil: XML uç noktasında
LIBXML_NOENT'i kaldır,LIBXML_NONETekle; DOCTYPE reddi devreye al. WAF'ta<!ENTITY/SYSTEMiçin geçici kural. - Geçici: Giden ağ loglarını inceleyerek SSRF/veri sızıntısı kapsamını belirle; sızmış olabilecek dosyaları/sırları (özellikle bulut kimlik bilgileri) döndür.
- Kalıcı: Ayrıştırma katmanını güvenli bayraklarla merkezîleştir; sürümleri güncelle; XML gerekmiyorsa JSON'a geç; testler ve SAST kuralları ekle.
15. Checklist
- [ ] Hiçbir XML ayrıştırma çağrısı
LIBXML_NOENTiçermiyor mu? - [ ]
LIBXML_NONETveriliyor mu (ağ erişimi kapalı)? - [ ] Uygulama DTD beklemiyorsa DOCTYPE içeren girdi reddediliyor mu?
- [ ]
libxml_disable_entity_loader(..., false)gibi savunmayı kapatan çağrı var mı? (Olmamalı.) - [ ] PHP 7.x dağıtımında
libxml_disable_entity_loader(true)çağrılıyor mu? - [ ] XML zorunlu mu, yoksa JSON kullanılabilir mi?
- [ ] Dosya yükleme/meta veri/SAML/SOAP gibi dolaylı XML işleyiciler denetlendi mi?
- [ ] Ayrıştıran süreç en az yetkili mi; iç ağa/sırlara erişimi kısıtlı mı?
- [ ] XML işleyen uygulamadan beklenmeyen giden istekler için SIEM alarmı var mı?
16. Laboratuvar
Lab 10.1 — NOENT tuzağı. İzole ortamda aynı DOMDocument->loadXML çağrısını önce LIBXML_NOENT ile, sonra LIBXML_NONET ile çalıştır. Zararsız bir yerel dosyayı (kendi oluşturduğun bir test.txt) işaret eden bir varlıkla, ilkinin dosyayı çözdüğünü, ikincisinin çözmediğini gözlemle.
Lab 10.2 — Sürüm davranışı. PHP 7.x ve 8.x ortamlarında (varsa) aynı kodu çalıştırıp libxml varsayılan davranış farkını gözlemle. libxml_disable_entity_loader'ın 8'de neden deprecated olduğunu açıkla.
Lab 10.3 — DOCTYPE reddi. DOCTYPE içeren girdiyi baştan reddeden bir kontrol yaz; meşru XML'in geçtiğini, DOCTYPE'lı olanın reddedildiğini doğrula.
Lab 10.4 — Dolaylı vektör farkındalığı. Bir .docx/.svg dosyasının aslında zip/XML olduğunu inceleyip, "sadece dosya yüklüyorum" varsayımının neden XXE yüzeyi olabileceğini yaz.
17. Quiz
- XXE'nin kök nedeni nedir? Hangi meşru XML özelliği kötüye kullanılır?
- Dahili, harici genel ve harici parametre varlıkları arasındaki fark nedir?
LIBXML_NOENTbayrağı adının aksine ne yapar? Neden XXE'nin en yaygın nedenidir?libxml2.9.0 ve PHP 8.0, XXE varsayılanları açısından neyi değiştirdi? Bu neden yeni bir tuzak yarattı?- XXE hangi üç ana etkiye açılır? Her birine PHP'de bir örnek verin.
- Kör (blind) XXE nedir ve görünür yanıt olmadan nasıl doğrulanır/sızdırılır?
php://filterbase64 sarmalayıcısı PHP'de dosya okumayı neden güçlendirir?- XXE, SSRF'e nasıl köprü olur? Bulut metadata neden özel bir hedeftir?
- CVE-2021-29447'de iki "güvenli görünen" karar nasıl birleşip açık üretti?
- Neden XXE savunması büyük ölçüde bir "yapılandırma/sürüm" meselesidir?
- XML yerine JSON önermek neden etkili bir önlemdir?
- SIEM'de XXE için en güçlü tespit sinyali nedir ve neden SSRF ile ortaktır?
18. Kaynakça
- OWASP, XML External Entity (XXE) Prevention Cheat Sheet.
- OWASP, Testing for XML Injection / XXE (WSTG).
- MITRE, CWE-611: Improper Restriction of XML External Entity Reference; CWE-776: XML Entity Expansion (Billion Laughs).
- MITRE / NVD, CVE-2021-29447 (WordPress Media XXE); CVE-2016-4449 (libxml2).
- SonarSource (Sonar), WordPress 5.7 XXE Security Vulnerability teknik analizi.
- PHP Manual,
DOMDocument::loadXML,simplexml_load_string,libxml_disable_entity_loaderve libxml sabitleri (LIBXML_NOENT, LIBXML_NONET, LIBXML_DTDLOAD). - libxml2 sürüm notları (2.9.0 harici varlık varsayılan değişikliği).