💡 Повний робочий приклад доступний на GitHub:
merge-untrusted-html-uploads-dotnet
Безпечне об’єднання недовірених HTML‑документів — це робочий процес GroupDocs.Merger для .NET, який відхиляє зовнішні ресурси, на які посилається завантаження — віддалені 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 не має конструктора без параметрів і є sealed з 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 };
Метод OnLoading обробника встановлює args.Skipped = false і args.Data у ці байти, коли запитана URI збігається, тому документ отримує логотип без будь‑якого запиту GroupDocs.Merger.
Коли ми запустили злиття за замовчуванням для завантажень рахунку та накладної, усі сім зовнішніх посилань у обох файлах були заблоковані — віддалена таблиця стилів, віддалений логотип, URL‑адреса у стилі метаданих, зображення у сусідській папці, шлях file:///, UNC‑шлях і трекінговий URL з рядком запиту. joined.html зберігає текст обох завантажень; для кожного заблокованого зображення вставляється заповнювач. Після підключення обробника довіреного логотипу логотип був «наданий», а інші шість залишилися «заблокованими».
Порівняння: до і після
| До (без параметра) | Після (GroupDocs.Merger 26.10) | |
|---|---|---|
| Зовнішні запити | Кожне посилання завантажується автоматично | Відхилено, якщо явно не дозволено |
| Абсолютні локальні файлові шляхи | Завантажено у вихідний файл | Відхилено, як будь‑яке інше посилання |
| Довірені виключення | Не потрібні — все завантажується | Один ресурс через обробник або дозволений префікс |
| Неправильний запис у whitelist | whitelist не існував | Відхилено з ArgumentException під час створення |
| Потрібен код | Немає способу обмежити завантаження | Кілька рядків у LoadOptions |
Контраст полягає в тому, що можливо, а не лише в тому, що зручно: до 26.10 не існувало налаштування, яке зупиняло завантаження; після нього відхилення стало значенням за замовчуванням, а кожне виключення має бути явно зазначене.
Чи порушить це HTML, який вже залежав від завантаження віддалених зображень?
Якщо такий HTML все ще зливається з LoadOptions за замовчуванням, так — зображення не завантажаться, і у вихідному файлі замість них з’явиться заповнювач. Виправлення має бути явним, а не обхідним шляхом: перелічіть довірені джерела у WhitelistedResources або підключіть IExternalResourceHandler, який постачає конкретні активи, які ви все ще хочете відобразити. У будь‑якому випадку злиття продовжуватиметься; просто перестане запитувати все, що ви не назвали.
Реальний приклад: консолідація рахунку‑фактури та накладної
Конвеєр документів, який об’єднує HTML, наданий клієнтом, у архівну копію, саме той сценарій, який моделює зразковий проєкт цієї сторінки: завантаження рахунку‑фактури та накладної з того ж замовлення, об’єднані в один файл. Жодне з завантажень не було написане командою конвеєра, тому їх не можна вважати безпечними для буквального читання. У рахунку вказана сума до сплати та клієнт Northwind Traders, а також посилання на логотип, трекінгову таблицю стилів, URL‑адресу у стилі метаданих хмари, зображення у тій же папці та шлях file:/// — сім зовнішніх посилань між двома завантаженнями, жодне з яких конвеєр архівації не повинен запитувати від імені клієнта.
Перед тим, як завдання злиття вийде в продакшн, варто визначити для кожного типу документу, чи потрібне взагалі якесь виключення. У цьому прикладі сценарію інвойсингу потрібен був лише один: власний логотип компанії, який подається з файлу на диску, а не запитується за віддаленою URL‑адресою, зазначеною в HTML. Все інше — трекінг‑таблиця стилів, адреса у стилі метаданих, файл‑сиблінг, 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/ викликає виключення з повідомленням Whitelisted resource 'cdn.example.com' must be an absolute URI without a query, fragment or encoded '/', '\' or '.'. (Parameter 'WhitelistedResources') — ще до відкриття будь‑якого завантаження. Виявлення неправильно налаштованого whitelist під час запуску варте більше, ніж виявлення його після того, як ресурс безшумно не завантажився у продакшн.
Що ще можна зробити з GroupDocs.Merger для .NET?
- Раннє валідовання записів whitelist: неправильний запис у
WhitelistedResourcesпризводить до помилки під час створенняMerger, тому перевірки під час старту розгортання виявляють її до обробки будь‑якого документа. - Постачання довірених активів з диска:
IExternalResourceHandlerможе відповісти на запит байтами, які вже є у вас, без жодного мережевого доступу. - Застосування однієї політики до кожного приєднаного документа:
LoadOptionsзадаються один раз наMerger, і кожен документ, приєднаний пізніше, дотримується тих самих правил. - Підтримка не лише HTML: документи обробки тексту, HTML/MHTML, презентації, електронні таблиці, SVG та EPUB також дотримуються тих самих параметрів.
Висновок
Об’єднання HTML, який ви не писали, більше не означає довіру до всього, що в ньому міститься. GroupDocs.Merger for .NET відхиляє зовнішні ресурси за замовчуванням, починаючи з версії 26.10, а два способи надати виключення — обробник довіреного активу та WhitelistedResources — дають саме такий рівень доступу, який потрібен вашому конвеєру, і нічого зайвого.
Готові спробувати самостійно?
- Спробуйте безкоштовну тимчасову ліцензію
- Прочитайте як пропускати завантаження зовнішніх ресурсів
- Дізнайтеся коли GroupDocs.Merger отримує доступ до мережі
- Перегляньте покроковий посібник по випадкам використання
- Ознайомтеся з зразковим проектом на GitHub