💡 完整可運作的範例可在 GitHub 上取得: merge-untrusted-html-uploads-dotnet
安全合併不受信任的 HTML 文件是針對 .NET 的 GroupDocs.Merger 工作流程,會拒絕上傳檔案所引用的外部資源──遠端 URL、UNC 路徑與本機檔案──除非您明確允許它們。
以往的做法很痛苦
想像您的開票平台將客戶上傳的發票與同一訂單的送貨單合併成一個 HTML 文件以供存檔。發票是純 HTML——包含樣式表連結、商標圖片、幾段文字——但這不是您寫的,而是客戶的瀏覽器或匯出工具產生的。在 GroupDocs.Merger 26.10 之前,合併會自動載入 HTML 所引用的外部資源──遠端樣式表、遠端圖片、絕對本機檔案路徑──且沒有任何方式可以阻止它。
當上傳的內容不完全可信時,這就成了問題。樣式表的 URL 也可能是追蹤像素。圖片的 src 可能指向 http://169.254.169.254/latest/meta-data/iam/security-credentials/──一個雲端元資料位址──而您的合併步驟會代表上傳者去請求它。絕對的 file:/// 路徑也會被讀取,於是另一位客戶的上傳檔案可能被拉入合併輸出。
盲目合併的真正代價:一個會抓取上傳檔案所要求的任何資源的合併作業,會把您的伺服器變成 SSRF 的代理,而
file:///參考則會讓一位客戶的合併讀取另一位客戶的檔案。
有更好的方法
GroupDocs.Merger for .NET 在預設情況下即解決此問題。自 26.10 版起,每個 LoadOptions 建構函式都會將 SkipExternalResources = true,而未提供載入選項建立的 Merger 也會使用相同的預設——除非您另行允許,否則不會載入任何外部資源。
先決條件:
- .NET(GroupDocs.Merger for .NET 支援的四個目標框架中的任意一個)
- GroupDocs.Merger for .NET 26.10 或更新版本(取得臨時授權)
- 兩個要合併的 HTML 文件,至少其中一個來源不完全可信
使用以下指令安裝:
dotnet add package GroupDocs.Merger --version 26.10.0
新方法:在不信任的情況下合併 HTML
以下示範如何在安全預設、記錄每個決策的處理器,以及在第二輪中提供唯一可信資產的處理器下,合併兩個 HTML 上傳檔案。
步驟 1 - 為格式建立 Load Options
LoadOptions 自 26.10 起沒有無參數建構函式且已封閉;範例明確指定格式。
// LoadOptions(FileType.HTML) already denies everything external by default
var audit = new ResourceAudit();
var loadOptions = new LoadOptions(FileType.HTML) { ExternalResourceHandler = audit };
步驟 2 - 合併上傳檔案並檢查決策
// Both uploads follow the options of the Merger they are joined to
using (var merger = new Merger(firstUpload, loadOptions))
{
merger.Join(secondUpload);
merger.Save(outputPath);
}
foreach (string decision in audit.Decisions)
{
Console.WriteLine(decision);
}
重點說明:
SkipExternalResources:此處隱含為true,因此所有遠端 URL、UNC 路徑與本機檔案參考皆被拒絕,甚至包括上傳資料夾內的檔案。- 每次合併使用一個
LoadOptions:第二個上傳檔案不會取得自己的選項——它會繼承建立它的Merger所使用的設定。
步驟 3 - 僅允許一個可信資源
// Supply the logo's bytes directly instead of requesting it over the network
var trustedAssets = new Dictionary<string, byte[]>
{
[logoUrl] = File.ReadAllBytes(logoPath)
};
var audit = new ResourceAudit(trustedAssets);
var loadOptions = new LoadOptions(FileType.HTML) { ExternalResourceHandler = audit };
當請求的 URI 符合時,處理器的 OnLoading 方法會將 args.Skipped = false 並將 args.Data 設為這些位元組,讓文件取得商標而不會讓 GroupDocs.Merger 進行任何網路請求。
我們以預設合併執行發票與送貨單上傳檔案時,兩個檔案中共七個外部參考全部被阻擋──遠端樣式表、遠端商標、元資料風格 URL、同層資料夾圖片、file:/// 路徑、UNC 路徑,以及帶查詢字串的追蹤 URL。joined.html 保留了兩個上傳檔案的文字;每個被阻擋的圖片都以佔位符取代。加入可信商標處理器後,商標被「供應」而其他六項仍保持「阻擋」狀態。
並排比較:前後對照
| 之前(無選項) | 之後(GroupDocs.Merger 26.10) | |
|---|---|---|
| 外部請求 | 每個參考自動載入 | 除非明確允許,否則全部拒絕 |
| 絕對本機檔案路徑 | 載入至輸出 | 如同其他參考一樣被拒絕 |
| 可信例外 | 不需要──全部載入 | 透過處理器或白名單前綴允許單一資源 |
| 錯誤的白名單條目 | 沒有白名單 | 建構時拋出 ArgumentException |
| 必要程式碼 | 無法限制載入 | 只需在 LoadOptions 上寫幾行 |
差異在於「可能」而非「方便」:26.10 之前沒有任何設定可以阻止載入;之後預設即為拒絕,所有例外必須明確聲明。
這會破壞已依賴載入遠端圖片的 HTML 嗎?
如果該 HTML 仍使用預設 LoadOptions 進行合併,答案是會——圖片不會被載入,輸出中會以佔位符取代。解決方式是明確的,而非變通:在 WhitelistedResources 中列出可信來源,或掛接 IExternalResourceHandler 供應您仍想呈現的特定資產。無論哪種方式,合併仍會繼續執行,只是停止抓取未被命名的任何資源。
真實案例:發票與送貨單合併
一條將客戶提交的 HTML 合併至存檔副本的文件管線正是本頁範例專案所模擬的情境:同一訂單的發票上傳與送貨單合併為單一檔案。兩個上傳檔案皆非管線團隊自行撰寫,故不能假設其內容安全。發票內含應付金額、客戶名稱(Northwind Traders),以及商標、追蹤樣式表、雲端元資料風格 URL、同層圖片與 file:/// 路徑——共七個外部參考,存檔管線不應代表客戶去抓取。
在合併作業正式上線前,建議逐文件類型決定是否需要例外。此範例的開票情境只需要一個例外:公司自己的商標,直接從磁碟讀取檔案,而非從 HTML 所指的遠端 URL 抓取。其他所有外部參考——追蹤樣式表、元資料位址、同層檔案、UNC 與 file:/// 路徑——皆無正當理由被抓取,因此僅使用預設設定即可滿足需求。
// Reject a whitelist entry that is not an absolute URI prefix
var loadOptions = new LoadOptions(FileType.HTML)
{
WhitelistedResources = new List<string> { "cdn.example.com" }
};
傳入僅主機名稱而非 https://cdn.example.com/brand/ 這類絕對 URI 前綴會拋出例外,訊息為 Whitelisted resource 'cdn.example.com' must be an absolute URI without a query, fragment or encoded '/', '\' or '.'. (Parameter 'WhitelistedResources')——在任何上傳檔案開啟之前即被檢測。於啟動時捕捉錯誤的白名單設定,比在生產環境中因資源靜默失敗才發現問題要有價值得多。
還能用 GroupDocs.Merger for .NET 做什麼?
- 提前驗證白名單條目:格式錯誤的
WhitelistedResources會在Merger建構時失敗,部署啟動檢查即可在任何文件處理前捕捉。 - 從磁碟供應可信資產:
IExternalResourceHandler能以您已擁有的位元組回應請求,完全不需網路存取。 - 對每個合併文件套用同一政策:
LoadOptions只需在Merger上設定一次,之後加入的所有文件皆遵循相同規則。 - 支援超過 HTML 的格式:文字處理、HTML/MHTML、簡報、試算表、SVG 與 EPUB 文件皆遵循相同選項。
結論
合併非您撰寫的 HTML 不再等同於必須信任其中的所有內容。自 26.10 版起,GroupDocs.Merger for .NET 預設拒絕外部資源,而透過可信資產處理器或 WhitelistedResources 兩種方式提供例外,讓您只給予管線所需的最小存取權限,絕不多餘。
準備好自己試試了嗎?
- 試用免費臨時授權
- 閱讀 如何跳過載入外部資源
- 了解 GroupDocs.Merger 何時存取網路
- 瀏覽 逐步使用案例指南
- 查看 GitHub 上的範例專案 (https://github.com/groupdocs-merger/merge-untrusted-html-uploads-dotnet)