💡 Plně funkční příklad dostupný na GitHubu: skip-external-resources-when-signing-dotnet
Úvod
Wordový dokument může obsahovat obrázek, který není součástí souboru. Dokument obsahuje adresu a vše, co jej otevře, tuto adresu načte. Na desktopu je to funkce – obrázek se aktualizuje, když se změní zdroj. Na serveru, který přijímá nahrané soubory, to znamená, že osoba, která vám soubor poslala, rozhoduje, které URL vaše infrastruktura požaduje.
Bezpečné načítání dokumentů je chování GroupDocs.Signature pro .NET, které odmítá provádět tyto požadavky. Od verze 26.9 je výchozí hodnota LoadOptions.SkipExternalResources nastavena na true. Tento článek porovnává tři režimy načítání na stejném dokumentu, ukazuje, jak povolit jeden hostitel bez povolení všech, a vysvětluje, proč podpis nedůvěryhodného souboru nepotřebuje žádný přístup k síti.
Proč je to důležitější, než to zní
Útok má název – server‑side request forgery (SSRF) – a tři konkrétní podoby.
Interní adresa, která není přístupná z internetu, je přístupná z vašeho serveru, takže upravený dokument může přimět vaši službu načíst http://169.254.169.254/ nebo administrativní endpoint na localhost a v závislosti na tom, co s výsledkem uděláte, jej uniknout. UNC cesta v dokumentu může přimět Windows hostitele k odchozímu ověření, čímž předá přihlašovací údaje serveru ovládanému útočníkem. A odkaz na hostitele, který nikdy neodpoví, zablokuje načítací vlákno až do vypršení časového limitu, což je levný způsob, jak vyčerpat pracovní pool.
Předpokládal jsem, že se jedná o teoretický problém, dokud jsem neviděl testovací dokument, který načítal obrázek přes službu, která vůbec nemá dělat odchozí požadavky. Nic z toho nevyžaduje chybu v knihovně dokumentů. Následování odkazu je to, co formát požaduje; otázkou je jen, zda by váš server měl vyhovět.
Metoda 1 – Nový výchozí
Žádné LoadOptions vůbec:
using var signature = new Signature(sourcePath);
return SavePagePreview(signature, previewPath);
Ničeho se nevyžádá. Náhled vykreslí prázdné místo tam, kde by byl odkazovaný obrázek, a PNG je menší, než by byl jinak. Tento rozdíl ve velikosti je nejpohodlnější důkaz, že žádný požadavek z počítače neodjel.
Které funkce se počítají jako externí? Odkazované obrázky místo vložených, pole INCLUDEPICTURE, odkazované obrázky v prezentacích a tabulkách a obrázky a stylové listy, na které odkazuje SVG. Vložený obsah zůstává nedotčený – už je součástí souboru.
Metoda 2 – Povolit jednu adresu
Mnoho dokumentů odkazuje na legitimní místo: firemní CDN, interní server s obrázky, úložiště šablon. Povolit to a nic dalšího:
var loadOptions = new LoadOptions
{
WhitelistedResources = new List<string> { trustedAddress }
};
using var signature = new Signature(sourcePath, loadOptions);
Pravidlo pro shodu si zaslouží pozornost. Jedná se o test podřetězce bez rozlišení velikosti písmen vůči adrese zdroje, což znamená, že krátký fragment je nebezpečný: github odpovídá github.attacker.example/payload.png stejně snadno jako hostiteli, který jste zamýšleli. Použijte schéma, hostitele a cestu – ve vzorku je na whitelistu raw.githubusercontent.com/groupdocs-signature/.
Metoda 3 – Povolit vše
Chování před verzí 26.9, stále dostupné:
var loadOptions = new LoadOptions { SkipExternalResources = false };
Rozumné pro dokumenty, které vytvořila vaše vlastní aplikace. Jeden úskalí, které stojí za upozornění: zastaralá vlastnost LoadExternalResources má opačnou polaritu, takže SkipExternalResources = false nahrazuje LoadExternalResources = true. Zkopírujete-li hodnotu ze staré vlastnosti, obrátíte svou bezpečnostní politiku bez jakékoli chyby, která by vás upozornila.
Porovnání tří metod: Kdy použít kterou
| Režim | Nejvhodnější pro | Klíčové výhody | Omezení |
|---|---|---|---|
| Výchozí (přeskočit) | nahrávání uživateli, e‑mail, soubory od partnerů | není možné provést žádný odchozí požadavek | odkazované obrázky se zobrazí jako zástupné symboly |
| Whitelist | dokumenty, které odkazují na hostitele, který vlastníte | zachovává funkčnost legitimních odkazů | porovnávání podřetězcem vyžaduje dlouhý, specifický fragment |
| Povolit vše | soubory generované vašimi systémy | náhledy vypadají přesně jako dříve | obnovuje zranitelnost SSRF, kterou výchozí nastavení odstranilo |
Co podpis – potřebuje to zdroje?
Ne, a to je praktický přínos. QR‑kódový podpis se aplikuje s výchozím nastavením načítání a během načítání, podepisování nebo ukládání dokumentu není požadován žádný externí zdroj:
var options = new QrCodeSignOptions("Approved by GroupDocs.Signature")
{
EncodeType = QrCodeTypes.QR,
Left = 400,
Top = 50,
Width = 120,
Height = 120
};
SignResult result = signature.Sign(outputPath, options);
Podepsaný výstup si zachová svůj odkaz, takže uživatel, který dokument později otevře, stále vidí obrázek načtený na svém vlastním počítači. Přeskočení je server‑side politika, ne úprava dokumentu – což je to, co dělá aplikaci na souborech, které zpracováváte jménem někoho jiného, bezpečnou.
Co se změní při aktualizaci
Pro většinu služeb není na první pohled nic viditelného, a to stojí za to jasně uvést, protože bezpečnostní výchozí nastavení, které mění chování všude, by neprošlo revizí aktualizace. Výjimkou je situace, kdy náhled nebo miniatura dříve zobrazovala odkazovaný obrázek a nyní zobrazuje zástupný symbol; to je změna, která plní svůj úkol, a řešením je položka na whitelistu, pokud je hostitel váš, nebo přijetí, pokud dokument pochází zvenčí.
Upřímný způsob, jak to ověřit, je ten, který používá ukázka: vykreslete stejný dokument ve všech třech režimech a porovnejte velikosti výstupů. Pokud jsou výchozí a whitelistované náhledy stejné velikosti, v žádném případě nebylo nic staženo – což obvykle znamená, že hostitel není z tohoto stroje dosažitelný, spíše než že whitelist selhal, a ukázka vytiskne tip, který to přesně říká.
Pomocník pro náhled, protože to není zřejmé
Dva ze tří výše uvedených režimů volají malý pomocník a stojí za to jej ukázat, protože PreviewOptions nepřijímá cestu:
var previewOptions = new PreviewOptions(
pageData => File.Create(previewPath),
(pageData, pageStream) => pageStream.Dispose())
{
PreviewFormat = PreviewOptions.PreviewFormats.PNG
};
signature.GeneratePreview(previewOptions);
Přijímá dvě továrny na streamy – jednu pro vytvoření streamu pro každou stránku, druhou pro jeho uvolnění. Vzorkový dokument má jednu stránku, takže se zapíše jeden soubor; u vícestránkového vstupu vložte číslo stránky do názvu souboru, jinak každá stránka přepíše předchozí.
Nejlepší postupy
- Považujte vše, co jste nevytvořili, za nedůvěryhodné, včetně souborů od partnerů s dobrým bezpečnostním postojem.
- Ujistěte se, že fragmenty whitelistu jsou dostatečně dlouhé a jednoznačné, a revidujte je při změně CDN.
- Nikdy nenastavujte
SkipExternalResourcesz hodnoty, která dříve byla přiřazenaLoadExternalResources. - Ověřujte pomocí velikostí výstupů spíše než nastavení; konfigurace, která vypadá správně, a požadavek, který se neuskutečnil, jsou odlišná tvrzení.
Kde to nechává SVG
Stojí za to zmínit zvlášť, protože SVG je jak běžný formát pro nahrávání, tak běžný vektor SSRF. SVG může odkazovat na obrázky a stylové listy pomocí URL a tyto odkazy jsou externí zdroje podle stejného pravidla – ve výchozím nastavení jsou přeskočeny, lze je zařadit na whitelist a obnovit. Služba, která přijímá SVG avatary nebo loga a vykresluje je na serveru, byla přesně tím typem systému, který tato změna chrání.
Pokud váš pipeline přijímá SVG od uživatelů, výchozí nastavení je to, které chcete, a whitelist slouží pro případ, kdy vaše vlastní šablony načítají sdílený stylový list z hostitele, který provozujete.
Závěr
Výchozí nastavení se změnilo tak, že rizikové chování vyžaduje explicitní rozhodnutí a bezpečné nevyžaduje nic. Zachovejte výchozí nastavení pro nedůvěryhodný vstup, úzce omezte whitelist tam, kde jsou zapojeny vaše vlastní hostitele, a pamatujte, že samotný podpis nikdy nepotřeboval síť. Spuštění ukázky na jednom z vašich vlastních dokumentů trvá minutu a řekne vám, ve třech velikostech souborů, přesně co vaše služba stahovala.