今天我們來深入探討一個在處理 XML 資料時極為致命的漏洞——XXE Injection。
XXE 的全名是 XML External Entity Injection,中文譯為「XML 外部實體注入」。這是一種伺服器端的攻擊,發生在應用程式解析 (Parse) 使用者提供的 XML 輸入時,如果 XML 解析器配置不當,允許處理外部實體的宣告,攻擊者就能夠發起攻擊。
這個漏洞的威力極大,它可以直接導致機敏資料外洩、伺服器端請求偽造 (SSRF),甚至是拒絕服務攻擊 (DoS)。
要理解 XXE,首先要了解 XML 的一個特性:實體 (Entity)。
一個致命的比喻:
想像你有一本神奇的「萬能說明書」(你的 Web 應用程式),它會忠實地執行你用特殊墨水 (XML 格式) 寫下的任何指令。
正常指令:你在說明書上寫下 <章節名>第一章</章節名>
,說明書就會顯示「第一章」。
惡意指令 (XXE):一個攻擊者用特殊墨水寫下一個指令:<!DOCTYPE foo [ <!ENTITY a_secret_file SYSTEM "file:///etc/passwd"> ]>
這個指令的意思是:「我現在定義一個名為 a_secret_file
的變數,它的內容請你去伺服器的 /etc/passwd
這個檔案裡讀取。」
接著,他又寫下:<章節內容>&a_secret_file;</章節內容>
這句話的意思是:「請把 a_secret_file
這個變數的內容顯示在這裡。」
盲目執行的說明書 (脆弱的 XML 解析器):
你的「萬能說明書」看到這些指令後,它不會思考這個指令是否惡意,只會忠實地:
/etc/passwd
檔案的內容。<章節內容>
的位置。攻擊就這樣發生了。脆弱的 XML 解析器就像那本盲目執行的說明書,它被攻擊者欺騙,從而洩漏了不該被存取的內部資訊。
XXE 的威力體現在它多樣化的攻擊向量上。
這是最直接的利用方式,透過 file://
協定讀取伺服器上的任意檔案。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE user [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<user>
<username>&xxe;</username>
<password>some_password</password>
</user>
&xxe;
實體。SYSTEM
的定義,讀取 /etc/passwd
的內容。&xxe;
。/etc/passwd
內容的 <username>
標籤回傳給攻擊者。XXE 是觸發 SSRF 的一種絕佳方式。攻擊者可以讓伺服器代替他向內部網路發起請求。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE stock_check [ <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLENAME"> ]>
<stock>
<product_id>&xxe;</product_id>
</stock>
http://169.254.169.254/...
(AWS 的內部中繼資料服務地址) 發起一個 HTTP 請求。這是一種利用實體遞迴引用的方式,在記憶體中產生海量資料,從而耗盡伺服器資源導致崩潰。
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;"> <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> ...
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;"> ]>
<lolz>&lol9;</lolz>
&lol9;
時,會嘗試將其展開。防禦 XXE 的核心思想非常明確:除非你的業務邏輯真的、真的、真的需要,否則就應該在你的 XML 解析器中徹底禁用外部實體的處理功能。
這是最根本、最有效的防禦措施。
Java
對於 JAXP、DOM、SAX 等標準庫,可以設定以下特性來禁用外部實體:
// 禁用 DTDs (這會間接禁用外部實體)
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// 或者更明確地禁用外部實體
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
PHP
使用 libxml 時,在解析前加入以下程式碼:
libxml_disable_entity_loader(true);
Python
對於 lxml
函式庫,在創建解析器時禁用實體解析:
# resolve_entities=False 是關鍵
xml_parser = etree.XMLParser(resolve_entities=False)
etree.fromstring(xml_string, xml_parser)
XXE 是一個高危險的伺服器端漏洞,它源於對 XML 這種老舊但仍廣泛使用的資料格式的特性處理不當。攻擊者可以透過注入惡意的外部實體定義,將原本只是用來傳輸資料的 XML 變成一個強大的攻擊武器。對於開發者而言,最關鍵的防禦措施就是在 XML 解析層面關閉這個危險的「後門」,永不處理來自不可信來源的 DTD 或外部實體。