💡 Pełny działający przykład dostępny na GitHubie:
skip-external-resources-when-signing-dotnet

Wprowadzenie

Dokument Word może zawierać obraz, który nie znajduje się w pliku. Dokument przechowuje adres, a każdy program, który go otwiera, pobiera ten adres. Na komputerze stacjonarnym jest to funkcja – obraz aktualizuje się, gdy zmieni się źródło. Na serwerze przyjmującym pliki oznacza to, że osoba, która wysłała Ci plik, decyduje, które adresy URL Twoja infrastruktura ma żądać.

Bezpieczne ładowanie dokumentów to zachowanie GroupDocs.Signature dla .NET, które odmawia wykonywania tych żądań. Od wersji 26.9, LoadOptions.SkipExternalResources domyślnie ma wartość true. Ten artykuł porównuje trzy tryby ładowania na tym samym dokumencie, pokazuje, jak zezwolić jednemu hostowi bez zezwalania na wszystkie, oraz wyjaśnia, dlaczego podpisywanie nieufnego pliku nie wymaga żadnego dostępu do sieci.

Dlaczego to jest ważniejsze niż się wydaje

Atak ma nazwę – server‑side request forgery (SSRF) – i trzy konkretne formy.

Adres wewnętrzny, który jest nieosiągalny z Internetu, jest osiągalny z Twojego serwera, więc spreparowany dokument może spowodować, że Twoja usługa pobierze http://169.254.169.254/ lub punkt końcowy administratora na localhost i, w zależności od tego, co z wynikiem zrobisz, wycieknie on. Ścieżka UNC w dokumencie może skłonić hosta Windows do uwierzytelnienia się na zewnątrz, przekazując poświadczenia atakującemu kontrolującemu serwer. A link do hosta, który po prostu nigdy nie odpowiada, blokuje wątek ładowania, aż nastąpi timeout, co jest tanim sposobem na wyczerpanie puli pracowników.

Zakładałem, że to jedynie teoretyczna obawa, dopóki nie zobaczyłem dokumentu testowego pobierającego obraz przez usługę, która w ogóle nie powinna wykonywać żądań wychodzących. Żadne z tego nie wymaga błędu w bibliotece dokumentów. Śledzenie linku to to, czego wymaga format; pytanie brzmi jedynie, czy Twój serwer powinien się na to zgodzić.

Metoda 1 – Nowy domyślny

Brak LoadOptions w ogóle:

using var signature = new Signature(sourcePath);
return SavePagePreview(signature, previewPath);

Nic nie jest pobierane. Podgląd renderuje pusty placeholder tam, gdzie znajdowałby się powiązany obraz, a plik PNG jest mniejszy niż w przeciwnym wypadku. Ta różnica w rozmiarze jest najwygodniejszym dowodem, że żadne żądanie nie opuściło maszyny.

Które funkcje liczą się jako zewnętrzne? Powiązane obrazy zamiast osadzonych, pola INCLUDEPICTURE, powiązane obrazy w prezentacjach i arkuszach kalkulacyjnych oraz obrazy i arkusze stylów, do których odwołuje się SVG. Zawartość osadzona pozostaje nietknięta – jest już w pliku.

Metoda 2 – Biała lista jednego adresu

Wiele dokumentów odwołuje się do legalnych źródeł: CDN firmy, wewnętrzny serwer obrazów, sklep szablonów. Zezwól na to i nic innego:

var loadOptions = new LoadOptions
{
    WhitelistedResources = new List<string> { trustedAddress }
};

using var signature = new Signature(sourcePath, loadOptions);

Reguła dopasowania wymaga uwagi. Jest to test podciągu (case‑insensitive) względem adresu zasobu, co oznacza, że krótki fragment jest niebezpieczny: github pasuje do github.attacker.example/payload.png tak samo łatwo, jak do hosta, którego zamierzałeś. Użyj schematu, hosta i ścieżki – w przykładzie biała lista zawiera raw.githubusercontent.com/groupdocs-signature/.

Metoda 3 – Zezwól na wszystko

Zachowanie sprzed wersji 26.9, nadal dostępne:

var loadOptions = new LoadOptions { SkipExternalResources = false };

Rozsądne dla dokumentów wygenerowanych przez własną aplikację. Jedna pułapka warta uwagi: przestarzała właściwość LoadExternalResources ma odwrotną polaryzację, więc SkipExternalResources = false zastępuje LoadExternalResources = true. Skopiujesz wartość ze starej właściwości i odwrócisz swoją postawę bezpieczeństwa bez żadnego komunikatu o błędzie.

Porównanie trzech trybów: Kiedy używać którego

Tryb Najlepszy dla Kluczowe zalety Ograniczenia
Domyślny (skip) przesyłane przez użytkowników, e‑mail, pliki partnerów żadne żądanie wychodzące nie jest możliwe powiązane obrazy wyświetlane są jako placeholdery
Biała lista dokumenty odwołujące się do hosta, który kontrolujesz utrzymuje działanie legalnych linków dopasowanie podciągiem wymaga długiego, specyficznego fragmentu
Zezwól na wszystko pliki wygenerowane przez własne systemy podglądy wyglądają dokładnie tak jak wcześniej przywraca ryzyko SSRF, które usunięto w domyślnym ustawieniu

Czy podpisywanie potrzebuje tych zasobów?

Nie, a to jest praktyczna korzyść. Podpis QR‑code jest stosowany z domyślnymi ustawieniami ładowania i żaden zewnętrzny zasób nie jest żądany podczas ładowania, podpisywania czy zapisywania dokumentu:

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);

Podpisany wynik zachowuje swój link, więc użytkownik otwierający dokument później nadal widzi obraz rozwiązywany na własnym komputerze. Pomijanie to polityka po stronie serwera, a nie edycja dokumentu – co czyni ją bezpieczną do zastosowania w plikach obsługiwanych w imieniu kogoś innego.

Co się zmienia po aktualizacji

Dla większości usług nic widocznego na pierwszy rzut oka, i warto to jasno stwierdzić, ponieważ domyślne ustawienie bezpieczeństwa, które zmienia zachowanie wszędzie, nie przetrwa przeglądu aktualizacji. Wyjątek to sytuacje, w których podgląd lub miniatura wcześniej wyświetlały powiązany obraz, a teraz pokazują placeholder; to właśnie zmiana spełnia swoją rolę, a rozwiązaniem jest wpis do białej listy, jeśli host jest Twój, lub akceptacja, jeśli dokument pochodzi z zewnątrz.

Uczciwy sposób sprawdzenia to ten, którego używa przykład: renderuj ten sam dokument we wszystkich trzech trybach i porównaj rozmiary wyników. Jeśli podglądy domyślne i z białą listą mają identyczny rozmiar, nic nie zostało pobrane w żadnym z przypadków – co zazwyczaj oznacza, że host jest nieosiągalny z tej maszyny, a nie że biała lista zawiodła, i przykład wypisuje wskazówkę mówiącą dokładnie to.

Pomocnik podglądu, ponieważ nie jest to oczywiste

Dwa z trzech trybów wywołują małego pomocnika i warto go pokazać, ponieważ PreviewOptions nie przyjmuje ścieżki:

var previewOptions = new PreviewOptions(
    pageData => File.Create(previewPath),
    (pageData, pageStream) => pageStream.Dispose())
{
    PreviewFormat = PreviewOptions.PreviewFormats.PNG
};

signature.GeneratePreview(previewOptions);

Przyjmuje dwie fabryki strumieni – jedną do tworzenia strumienia na stronę, drugą do jego zwolnienia. Przykładowy dokument ma jedną stronę, więc tworzony jest jeden plik; przy wielostronicowym wejściu umieść numer strony w nazwie pliku, w przeciwnym razie każda strona nadpisze poprzednią.

Najlepsze praktyki

  • Traktuj wszystko, czego nie wygenerowałeś, jako nieufne, w tym pliki od partnerów o solidnych postawach bezpieczeństwa.
  • Twórz fragmenty białej listy wystarczająco długie, aby były jednoznaczne, i przeglądaj je, gdy CDN się zmieni.
  • Nigdy nie ustawiaj SkipExternalResources na podstawie wartości, która wcześniej była przypisana do LoadExternalResources.
  • Weryfikuj za pomocą rozmiarów wyjściowych, a nie samego ustawienia; konfiguracja, która wygląda poprawnie, i brak żądania to różne twierdzenia.

Gdzie to zostawia SVG

Warto wymienić osobno, ponieważ SVG jest zarówno popularnym formatem do przesyłania, jak i częstym wektorem SSRF. SVG może odwoływać się do obrazów i arkuszy stylów przez URL, a te odwołania są zewnętrznymi zasobami według tej samej reguły – domyślnie pomijane, możliwe do dodania do białej listy, możliwe do przywrócenia. Usługa przyjmująca awatary lub loga w formacie SVG i renderująca je po stronie serwera była dokładnie tym scenariuszem, który ta zmiana chroni.

Jeśli Twój pipeline przyjmuje SVG od użytkowników, domyślne ustawienie jest tym, którego potrzebujesz, a biała lista przydaje się w sytuacji, gdy własne szablony pobierają współdzielony arkusz stylów z hosta, który prowadzisz.

Zakończenie

Domyślne zachowanie zostało odwrócone, tak aby ryzykowne działanie wymagało wyraźnej decyzji, a bezpieczne – niczego. Trzymaj domyślne ustawienie dla nieufnych danych, wąsko definiuj białą listę tam, gdzie zaangażowane są Twoje własne hosty, i pamiętaj, że samo podpisywanie nigdy nie wymagało sieci. Uruchomienie przykładu na jednym z własnych dokumentów zajmuje minutę i w trzech rozmiarach plików dokładnie pokaże, co Twoja usługa pobierała.

Dodatkowe zasoby