💡 Voll funktionsfähiges Beispiel auf GitHub verfügbar: merge-untrusted-html-uploads-dotnet

Sicheres Zusammenführen von nicht vertrauenswürdigen HTML‑Dokumenten ist ein GroupDocs.Merger‑Workflow für .NET, der die externen Ressourcen, auf die ein Upload verweist – entfernte URLs, UNC‑Pfade und lokale Dateien – verweigert, sofern Sie sie nicht ausdrücklich zulassen.

Der alte Weg war schmerzhaft

Stellen Sie sich vor, Ihr Rechnungs‑Portal fügt den Rechnungs‑Upload eines Kunden mit einem Lieferschein derselben Bestellung zu einem HTML‑Dokument für die Archivierung zusammen. Die Rechnung ist reines HTML – ein Stylesheet‑Link, ein Logo‑Bild, ein paar Absätze – nur dass Sie sie nicht geschrieben haben, sondern der Browser oder das Export‑Tool des Kunden. Vor GroupDocs.Merger 26.10 lud der Merge‑Vorgang die externen Ressourcen, die das HTML referenzierte – entfernte Stylesheets, entfernte Bilder, absolute lokale Dateipfade – und es gab keine Möglichkeit, das zu verhindern.

Das ist ein Problem, sobald der Upload nicht vollständig vertrauenswürdig ist. Eine Stylesheet‑URL kann auch ein Tracking‑Pixel sein. Ein Bild‑src kann auf http://169.254.169.254/latest/meta-data/iam/security-credentials/ zeigen – eine Cloud‑Metadaten‑Adresse – und Ihr Merge‑Schritt würde sie im Namen des Uploaders anfordern. Auch ein absoluter file:///‑Pfad wurde gelesen, sodass ein Verweis auf den Upload eines anderen Kunden diese Datei in die zusammengeführte Ausgabe einziehen konnte.

Die wahren Kosten des blinden Mergings: Ein Merge‑Job, der alles abruft, was ein Upload verlangt, macht Ihren Server zu einem Proxy für SSRF, und ein file:///‑Verweis wird zu einem Weg, über den ein Kunden‑Merge die Datei eines anderen Kunden liest.

Es gibt einen besseren Weg

GroupDocs.Merger für .NET behebt das standardmäßig. Seit Version 26.10 setzt jeder LoadOptions‑Konstruktor SkipExternalResources = true, und ein ohne Load‑Optionen erstellter Merger verwendet dieselbe Vorgabe – nichts Externes wird geladen, sofern Sie es nicht ausdrücklich erlauben.

Voraussetzungen:

  • .NET (irgendeines der vier Ziel‑Frameworks, die GroupDocs.Merger für .NET unterstützt)
  • GroupDocs.Merger für .NET 26.10 oder später (temporäre Lizenz erhalten)
  • Zwei HTML‑Dokumente zum Zusammenführen, mindestens eines aus einer Quelle, der Sie nicht vollständig vertrauen

Installation mit einem Befehl:

dotnet add package GroupDocs.Merger --version 26.10.0

Der neue Weg: HTML zusammenführen, ohne ihm zu vertrauen

So sieht das Zusammenführen zweier HTML‑Uploads mit den sicheren Vorgaben, einem Handler, der jede Entscheidung protokolliert, und – in einem zweiten Durchlauf – einem Handler, der ein vertrauenswürdiges Asset bereitstellt, aus.

Schritt 1 – Load‑Optionen für das Format erstellen

LoadOptions hat keinen parameterlosen Konstruktor und ist seit 26.10 versiegelt; das Beispiel nennt das Format explizit.

// LoadOptions(FileType.HTML) already denies everything external by default
var audit = new ResourceAudit();
var loadOptions = new LoadOptions(FileType.HTML) { ExternalResourceHandler = audit };

Schritt 2 – Uploads zusammenführen und Entscheidungen prüfen

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

Schlüsselpunkte:

  • SkipExternalResources: implizit true hier, sodass jede entfernte URL, jeder UNC‑Pfad und jede lokale Dateireferenz verweigert wird, einschließlich einer Datei im eigenen Ordner des Uploads.
  • Ein LoadOptions pro Join: Der zweite Upload erhält keine eigenen Optionen – er erbt, was auch immer beim Merger, zu dem er hinzugefügt wurde, konfiguriert war.

Schritt 3 – genau eine vertrauenswürdige Ressource zulassen

// 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 };

Die OnLoading‑Methode des Handlers setzt args.Skipped = false und args.Data auf diese Bytes, wenn die angeforderte URI übereinstimmt, sodass das Dokument das Logo erhält, ohne dass GroupDocs.Merger irgendwo eine Anfrage stellt.

Als wir den Standard‑Merge mit den Rechnungs‑ und Lieferschein‑Uploads ausführten, wurden alle sieben externen Verweise in beiden Dateien blockiert – das entfernte Stylesheet, das entfernte Logo, die Metadaten‑URL, das Bild im Nachbar‑Ordner, ein file:///‑Pfad, ein UNC‑Pfad und eine Tracking‑URL mit Abfragezeichenfolge. joined.html enthält den Text beider Uploads; ein Platzhalter steht für jedes blockierte Bild. Mit dem angehängten vertrauenswürdigen‑Logo‑Handler wurde das Logo supplied zurückgeliefert und die anderen sechs blieben blocked.

Gegenüberstellung: Vorher vs. Nachher

Vorher (keine Option) Nachher (GroupDocs.Merger 26.10)
Externe Anfragen Jeder Verweis wurde automatisch geladen Verweigert, sofern nicht ausdrücklich erlaubt
Absolute lokale Dateipfade In die Ausgabe geladen Verweigert wie jeder andere Verweis
Vertrauenswürdige Ausnahmen Nicht nötig – alles geladen Eine Ressource über einen Handler oder ein Whitelist‑Präfix
Fehlerhafter Whitelist‑Eintrag Keine Whitelist vorhanden Abgelehnt mit ArgumentException beim Erzeugen
Erforderlicher Code Keine Möglichkeit, das Laden zu beschränken Ein paar Zeilen zu LoadOptions

Der Unterschied liegt darin, was möglich ist, nicht nur darin, was bequem ist: Vor 26.10 gab es keine Einstellung, die das Laden stoppte; danach ist das Verweigern die Vorgabe und jede Ausnahme muss explizit angegeben werden.

Wird dies HTML brechen, das bereits vom Laden entfernter Bilder abhängt?

Wenn dieses HTML noch mit den Standard‑LoadOptions zusammengeführt wird, ja – die Bilder werden nicht geladen und ein Platzhalter nimmt im Ergebnis ihren Platz ein. Die Lösung ist explizit, nicht ein Workaround: Listen Sie die vertrauenswürdigen Quellen in WhitelistedResources auf oder hängen Sie einen IExternalResourceHandler an, der die spezifischen Assets liefert, die Sie noch rendern möchten. So oder so läuft der Merge weiter; er stoppt nur das Abrufen von allem, was Sie nicht benannt haben.

Praxisbeispiel: Konsolidierung von Rechnung und Lieferschein

Eine Dokument‑Pipeline, die von Kunden eingereichtes HTML in eine Archivkopie zusammenführt, ist genau das Szenario, das das Beispielprojekt dieser Seite modelliert: ein Rechnungs‑Upload und ein Lieferschein derselben Bestellung, zu einer Datei zusammengeführt. Keiner der Uploads stammt von Ihrem eigenen Team, also kann keiner als sicher angenommen werden. Die Rechnung nennt einen fälligen Betrag und einen Kunden, Northwind Traders, und enthält ein Logo‑Verweis, ein Tracking‑Stylesheet, eine Cloud‑Metadaten‑URL, ein Bild im selben Ordner und einen file:///‑Pfad – sieben externe Verweise zwischen den beiden Uploads, die die Archiv‑Pipeline nicht im Namen des Kunden abrufen sollte.

Bevor der Merge‑Job live geht, sollte man dokumentenweise entscheiden, ob überhaupt Ausnahmen nötig sind. Das Beispiel‑Szenario für die Rechnungsstellung benötigte genau eine: das firmeneigene Logo, das von einer bereits auf der Festplatte vorhandenen Datei bedient wird, anstatt von der entfernten URL, die das HTML nennt. Alles andere – das Tracking‑Stylesheet, die Metadaten‑Adresse, die Nachbar‑Datei, die UNC‑ und file:///‑Pfade – hatte keinen legitimen Grund, abgerufen zu werden, sodass die Vorgaben allein die richtige Antwort waren.

// Reject a whitelist entry that is not an absolute URI prefix
var loadOptions = new LoadOptions(FileType.HTML)
{
    WhitelistedResources = new List<string> { "cdn.example.com" }
};

Die Übergabe eines bloßen Hostnamens anstelle eines absoluten Präfixes wie https://cdn.example.com/brand/ löst eine Ausnahme aus, mit der Meldung Whitelisted resource 'cdn.example.com' must be an absolute URI without a query, fragment or encoded '/', '\' or '.'. (Parameter ‘WhitelistedResources’) – noch bevor einer der Uploads geöffnet wird. Das frühzeitige Erkennen einer fehlerhaften Whitelist beim Starten ist mehr wert, als sie erst nach einem stillschweigenden Fehlschlag in der Produktion zu entdecken.

Was können Sie sonst noch mit GroupDocs.Merger für .NET tun?

  • Whitelist‑Einträge frühzeitig validieren: Ein fehlerhaftes WhitelistedResources‑Element schlägt beim Erzeugen des Merger fehl, sodass Start‑Checks einer Bereitstellung es abfangen, bevor ein Dokument verarbeitet wird.
  • Vertrauenswürdige Assets von der Festplatte bereitstellen: IExternalResourceHandler kann eine Anfrage mit Bytes beantworten, die Sie bereits besitzen, ohne jeglichen Netzwerkzugriff.
  • Eine Richtlinie auf jedes zusammengeführte Dokument anwenden: LoadOptions wird einmal am Merger gesetzt, und jedes später hinzugefügte Dokument folgt denselben Regeln.
  • Mehr als HTML abdecken: Textverarbeitung, HTML/MHTML, Präsentationen, Tabellenkalkulationen, SVG‑ und EPUB‑Dokumente honorieren alle dieselben Optionen.

Fazit

HTML, das Sie nicht geschrieben haben, zusammenzuführen bedeutet nicht mehr, allem darin zu vertrauen. GroupDocs.Merger für .NET verweigert seit Version 26.10 standardmäßig externe Ressourcen, und die beiden Möglichkeiten, eine Ausnahme zu gewähren – ein vertrauenswürdiger‑Asset‑Handler und WhitelistedResources – geben Ihnen genau so viel Zugriff, wie Ihre Pipeline benötigt, nicht mehr.

Bereit, es selbst auszuprobieren?

Zusätzliche Ressourcen