💡 Volledig werkend voorbeeld beschikbaar op GitHub:
skip-external-resources-when-signing-dotnet

Introductie

Een Word‑document kan een afbeelding bevatten die zich niet in het bestand bevindt. Het document bevat een adres, en wat het ook opent, haalt dat adres op. Op een desktop is dit een functie – de afbeelding wordt bijgewerkt wanneer de bron dat doet. Op een server die uploads accepteert, betekent dit dat de persoon die je het bestand heeft gestuurd beslist welke URL‑s jouw infrastructuur opvraagt.

Veilig document laden is een GroupDocs.Signature‑gedrag voor .NET dat weigert die verzoeken te doen. Vanaf versie 26.9 staat LoadOptions.SkipExternalResources standaard op true. Dit artikel vergelijkt de drie laadmodi met hetzelfde document, laat zien hoe je één host kunt toestaan zonder alle hosts toe te staan, en behandelt waarom ondertekenen van een onbetrouwbaar bestand helemaal geen netwerktoegang nodig heeft.

Waarom dit belangrijker is dan het klinkt

De aanval heeft een naam – server‑side request forgery – en drie concrete vormen.

Een intern adres dat van het internet onbereikbaar is, is wel bereikbaar vanaf jouw server, zodat een gemanipuleerd document jouw service kan laten ophalen http://169.254.169.254/ of een admin‑endpoint op localhost en, afhankelijk van wat je met het resultaat doet, dit kan lekken. Een UNC‑pad in een document kan een Windows‑host ertoe brengen uitgaand te authenticeren, waardoor inloggegevens aan een aanvaller‑gecontroleerde server worden overhandigd. En een link naar een host die simpelweg nooit antwoordt, blokkeert de laadthread tot deze time‑out, wat een goedkope manier is om een worker‑pool uit te putten.

Ik ging er eerst van uit dat dit een theoretisch probleem was, totdat ik een testdocument zag dat een afbeelding ophaalde via een service die helemaal geen uitgaande verzoeken zou moeten doen. Niets hiervan vereist een bug in de documentbibliotheek. Het volgen van een link is wat het formaat vraagt; de vraag is alleen of jouw server daaraan moet voldoen.

Methode 1 – De nieuwe standaard

Geen LoadOptions whatsoever:

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

Er wordt niets opgehaald. De preview rendert een lege tijdelijke aanduiding waar de gekoppelde afbeelding zou staan, en de PNG is kleiner dan wanneer de afbeelding wel zou worden geladen. Dat grootteverschil is het meest overtuigende bewijs dat er geen verzoek de machine heeft verlaten.

Welke functies tellen als extern? Gekoppelde afbeeldingen in plaats van ingesloten, INCLUDEPICTURE‑velden, gekoppelde afbeeldingen in presentaties en spreadsheets, en de afbeeldingen en stijlbladen die een SVG refereert. Ingesloten inhoud blijft onaangeroerd – ze staat al in het bestand.

Methode 2 – Eén adres op de whitelist zetten

Veel documenten linken naar een legitieme bron: een bedrijfs‑CDN, een interne afbeeldingsserver, een sjabloon‑store. Sta dat toe en niets anders:

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

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

De overeenkomende regel verdient aandacht. Het is een case‑insensitive substring‑test tegen het resource‑adres, wat betekent dat een kort fragment gevaarlijk is: github komt overeen met github.attacker.example/payload.png net zo gemakkelijk als met de host die je bedoeld had. Gebruik een schema, een host en een pad – het voorbeeld whitelist‑t raw.githubusercontent.com/groupdocs-signature/.

Methode 3 – Alles toestaan

Het gedrag vóór versie 26.9, nog steeds beschikbaar:

var loadOptions = new LoadOptions { SkipExternalResources = false };

Redelijk voor documenten die jouw eigen applicatie heeft geproduceerd. Eén valkuil die het vermelden waard is: de verouderde eigenschap LoadExternalResources heeft de tegenovergestelde polariteit, dus SkipExternalResources = false vervangt LoadExternalResources = true. Kopieer je een waarde van de oude eigenschap, dan keer je je beveiligingshouding om zonder foutmelding.

Vergelijking van de drie: wanneer gebruik je welke

Modus Beste voor Belangrijkste voordelen Beperkingen
Standaard (skip) gebruikers‑uploads, e‑mail, partner‑bestanden geen uitgaand verzoek mogelijk gekoppelde afbeeldingen worden weergegeven als placeholders
Whitelist documenten die linken naar een host die jij bezit houdt legitieme links werkend substring‑matching vereist een lang, specifiek fragment
Alles toestaan bestanden die jouw eigen systemen hebben gegenereerd previews zien er exact uit als voorheen herstelt de SSRF‑blootstelling die de standaard heeft verwijderd

Wat betreft ondertekenen – heeft dat de resources nodig?

Nee, en dat is de praktische meerwaarde. Een QR‑code‑handtekening wordt toegepast met de standaard laadinstellingen en er wordt geen externe resource opgevraagd terwijl het document wordt geladen, ondertekend of opgeslagen:

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

De ondertekende output behoudt zijn link, zodat een gebruiker die later het document opent nog steeds de afbeelding ziet die op zijn eigen machine wordt opgehaald. Skipping is een server‑side beleid, geen wijziging aan het document – en dat maakt het veilig om toe te passen op bestanden die je namens een ander verwerkt.

Wat verandert er bij een upgrade

Voor de meeste services gebeurt er in eerste instantie niets zichtbaar, en dat is het waard om duidelijk te vermelden omdat een beveiligingsstandaard die overal gedrag wijzigt, niet zou overleven bij een upgrade‑review. De uitzondering is overal waar een preview of thumbnail vroeger een gekoppelde afbeelding liet zien en nu een placeholder toont; dat is de wijziging die zijn werk doet, en de oplossing is een whitelist‑item als de host van jou is, of acceptatie als het document van buitenaf komt.

De eerlijke manier om te controleren is die die het voorbeeld gebruikt: render hetzelfde document onder alle drie de modi en vergelijk de output‑groottes. Als de standaard‑ en whitelist‑previews identiek in grootte zijn, is er in geen van beide gevallen iets opgehaald – wat meestal betekent dat de host onbereikbaar is vanaf die machine in plaats van dat de whitelist gefaald heeft, en het voorbeeld geeft een hint die precies dat zegt.

De Preview‑helper, omdat het niet vanzelfsprekend is

Twee van de drie bovenstaande modi roepen een kleine helper aan, en het is de moeite waard om die te laten zien omdat PreviewOptions geen pad accepteert:

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

signature.GeneratePreview(previewOptions);

Hij neemt twee stream‑factories – één om een stream per pagina te maken, één om die weer vrij te geven. Het voorbeeld‑document heeft één pagina, dus er wordt één bestand geschreven; bij invoer met meerdere pagina’s moet je het paginanummer in de bestandsnaam opnemen, anders wordt elke pagina de vorige overschreven.

Best practices

  • Beschouw alles wat je niet zelf hebt gegenereerd als onbetrouwbaar, inclusief bestanden van partners met een goede beveiligingshouding.
  • Maak whitelist‑fragmenten lang genoeg om ondubbelzinnig te zijn, en evalueer ze wanneer een CDN verandert.
  • Stel SkipExternalResources nooit in op een waarde die vroeger aan LoadExternalResources was toegewezen.
  • Verifieer met output‑groottes in plaats van met de instelling; een configuratie die er goed uitziet en een verzoek dat niet heeft plaatsgevonden, zijn verschillende beweringen.

Waar dit SVG achterlaat

Het is de moeite waard om apart te benoemen, want SVG is zowel een veelgebruikt upload‑formaat als een veelvoorkomende SSRF‑vector. Een SVG kan afbeeldingen en stijlbladen refereren via URL, en die referenties zijn externe resources onder dezelfde regel – standaard overgeslagen, whitelist‑baar, herstelbaar. Een service die SVG‑avatars of -logo’s accepteert en server‑side rendert, is precies het type systeem dat deze wijziging beschermt.

Als jouw pipeline SVG‑bestanden van gebruikers accepteert, is de standaardinstelling wat je wilt, en de whitelist is voor het geval jouw eigen sjablonen een gedeeld stylesheet van een host die jij beheert ophalen.

Conclusie

De standaard is omgedraaid zodat het riskante gedrag een expliciete beslissing vereist en het veilige gedrag niets nodig heeft. Houd de standaard aan voor onbetrouwbare invoer, whitelist nauwkeurig waar jouw eigen hosts betrokken zijn, en onthoud dat ondertekenen zelf nooit netwerktoegang nodig had. Het uitvoeren van het voorbeeld tegen één van jouw eigen documenten duurt een minuut en vertelt je, in drie bestandsgroottes, precies wat jouw service heeft opgehaald.

Aanvullende bronnen