XML 文檔的物理結構就像一連串的一些文件,每個文件都由 Unicode 字符序列組成,但這些文件無法與元素和屬性的邏輯模型完美地聯系起來,這些元素和屬性都是通過 API(例如,SAX 和 DOM)暴露的。結構與模型的分離足以使意志堅定的黑客打開一個較大的漏洞。因此,高安全性應用程序需要對它們可以處理的良好格式的 XML 文檔加以限制。
實體解析
實體解析(包括讀取外部 DTD 子集)打開了 XML 中的一些潛在安全漏洞。它們雖然不是什麼重大漏洞,但也足以造成一些麻煩。例如,XML 文檔可能指向一個外部 DTD。當解析器讀取文檔時,它通常會加載這個外部文件。您需要著重考慮以下三個問題:
XML bug:外部 DTD 所駐留的站點會記錄該通信。該站點會知道文件已經被讀取。由此帶來的影響類似於使用外部映像跟蹤電子郵件。我沒聽說過任何將傳入信息解析為 XML 文檔的電子郵件客戶端,但許多系統都接受並解析各種來源的 XML 文檔。
拒絕服務:駐留 DTD 的站點可能由於處理 DTD 遲緩,而減緩 DTD 解析。如果處理得是畸形 DTD,甚至可能完全停止解析。
事後進行文檔修改: 如果遠程站點修改了 DTD,那麼它可以使用默認的屬性值,將新的內容注入到無法原樣呈現的文檔中。它可以通過重新定義實體引用來更改文檔的內容。
如果您的站點依賴於這樣的文檔,那麼您可以通過聯合使用以下幾種技術來防止上述問題:
您可以在第一次接收文檔時完全解析該文檔,並只存儲已完全分解的文檔。站點不應該存儲任何沒被預定義的實體引用(即除了 &、<、>、' 和 " 之外的所有引用)。
如果需要重復進行有效性驗證,那麼可以在存儲文檔時刪除文檔類型聲明。
如果需要重復進行有效性驗證,那麼應該在本地緩存並存儲 DTD,這樣,遠程更改就不會影響它。您可以使用目錄文件將所有針對 DTD 遠程副本的請求重定向到本地緩存。如此一來,就可以通過減少 WAN 流量,從側面提高性能。
無需進行外部 DTD 子集處理,就可以解析文檔。在 SAX 中,通過將 http://xml.org/sax/features/external-general-entitIEs 和 http://XML.org/sax/features/external-parameter-entitIEs 的特性設置為 false,可以指示解析器不去讀取外部 DTD 子集。例如:
parser.setFeature("http://XML.org/sax/features/external-general-entitIEs", false);
parser.setFeature("http://XML.org/sax/features/external-parameter-entitIEs", false);
如果您將這些特性更改為 false,那麼請注意,不要打開 DTD 有效性驗證。如果打開 DTD 有效性驗證,這兩個特性將被自動重置為 true。
另一種更復雜的方法是指定一個對所有實體總是返回 InputStream 的 EntityResolver。
這些防御措施有效地阻止了基於解析器創建外部網絡連接的所有攻擊。盡管如此,通過訪問解析器來讀取一個單獨的文檔,就可以進行拒絕服務(DOS)攻擊,無需任何其他連接。
溢出攻擊
XML 對元素名稱、實體深度和類似的東西沒有內建限制,所以攻擊者可以為這些構造體提供很長的值。這樣做會使編寫不夠嚴謹的程序出現緩沖區溢出錯誤(以及所有類似隱患)。使用 Java 編寫的程序對緩沖區溢出攻擊並不敏感,但這樣的攻擊會導致不斷地拋出意外異常或錯誤 —— 可能導致服務器或其他程序關閉。
攻擊者也可以根據指數只在內部 DTD 子集中有規律地建立實體引用,這樣,就會使小的輸入文檔制造出大量的文本。
比如說“狂笑攻擊”(清單 1),它可以損害基於 DOM 或另一個內存中 API 的系統。如果與屬性值沒有任何關聯,那麼這種攻擊甚至可以通過溢出字符串限制來損害基於 SAX 的系統。
清單 1. “狂笑攻擊”
<!DOCTYPE root [
<!ENTITY ha "Ha !">
<!ENTITY ha2 "&ha; &ha;">
<!ENTITY ha3 "&ha2; &ha2;">
<!ENTITY ha4 "&ha3; &ha3;">
<!ENTITY ha5 "&ha4; &ha4;">
...
<!ENTITY ha128 "&ha127; &ha127;">
]>
<root>&ha128;</root>
在 JAXP 1.3 中,這是與 Java 1.5 綁定在一起的,並且在早期的版本中,這是一個可用選項,您可以通過設置 SAX 特性 http://Javax.xml.XMLConstants/feature/secure-processing(XMLConstants.FEATURE_SECURE_PROCESSING),對所有潛在的溢出加以限制。一旦已經設置了該特性,任何過長的構造體 —— 無論是一個元素中的屬性太多,還是元素稱過長 —— 都將作為良好格式方面的錯誤對待。這意味著您可以不再拒絕那些格式良好的文檔,然而,默認值非常大,能夠處理大多數實際應用中的文檔。
結束語
並不需要對所有的程序都如此偏執。但是,如果您工作在一個對安全性要求很高的環境中,那麼您需要考慮本文所討論的各種可能性。