💡 Esempio completo funzionante disponibile su GitHub:
merge-untrusted-html-uploads-dotnet

Unire in modo sicuro documenti HTML non attendibili è un flusso di lavoro GroupDocs.Merger per .NET che nega le risorse esterne a cui un upload fa riferimento – URL remoti, percorsi UNC e file locali – a meno che non li si consenta esplicitamente.

Il vecchio modo era doloroso

Immagina che il tuo portale di fatturazione unisca l’upload della fattura di un cliente con una nota di consegna dello stesso ordine in un unico documento HTML per l’archiviazione. La fattura è HTML semplice – un collegamento a un foglio di stile, un’immagine del logo, un paio di paragrafi – tranne per il fatto che non l’hai scritta tu, ma il browser del cliente o lo strumento di esportazione l’hanno generata. Prima di GroupDocs.Merger 26.10, l’unione caricava le risorse esterne a cui l’HTML faceva riferimento – fogli di stile remoti, immagini remote, percorsi assoluti a file locali – e non c’era alcuna opzione per fermarlo.

Questo è un problema non appena l’upload non è completamente attendibile. Un URL di un foglio di stile può anche essere un pixel di tracciamento. Un src di immagine può puntare a http://169.254.169.254/latest/meta-data/iam/security-credentials/ – un indirizzo di metadati cloud – e il tuo passaggio di unione lo richiederebbe per conto dell’utente che ha effettuato l’upload. Anche un percorso assoluto file:/// veniva letto, quindi un riferimento a un upload di un altro cliente poteva inserire quel file nell’output unito.

Il vero costo di unire alla cieca: un lavoro di unione che recupera qualsiasi cosa l’upload richieda trasforma il tuo server in un proxy per SSRF, e un riferimento file:/// diventa un modo per una fusione di un cliente di leggere il file di un altro cliente.

C’è un modo migliore

GroupDocs.Merger per .NET risolve questo impostazione predefinita. Dalla versione 26.10, ogni costruttore di LoadOptions imposta SkipExternalResources = true, e un Merger creato senza opzioni di caricamento utilizza lo stesso valore predefinito – nessuna risorsa esterna viene caricata a meno che non lo si specifichi.

Prerequisiti:

  • .NET (qualsiasi dei quattro framework di destinazione supportati da GroupDocs.Merger per .NET)
  • GroupDocs.Merger per .NET 26.10 o successivo (ottieni una licenza temporanea)
  • Due documenti HTML da unire, almeno uno proveniente da una fonte non completamente attendibile

Installa con un comando:

dotnet add package GroupDocs.Merger --version 26.10.0

Il nuovo modo: Unire HTML senza fidarsi

Ecco come appare l’unione di due upload HTML con le impostazioni sicure, un gestore che registra ogni decisione e – in un secondo passaggio – un gestore che fornisce una risorsa attendibile.

Passo 1 - Creare le opzioni di caricamento per il formato

LoadOptions non ha un costruttore senza parametri ed è sealed dalla 26.10; l’esempio specifica esplicitamente il formato.

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

Passo 2 - Unire gli upload e ispezionare le decisioni

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

Punti chiave:

  • SkipExternalResources: implicitamente true qui, quindi ogni URL remoto, percorso UNC e riferimento a file locale viene negato, incluso un file nella cartella dell’upload stesso.
  • Una LoadOptions per unione: il secondo upload non ottiene le proprie opzioni – eredita quelle con cui è stato creato il Merger a cui è stato unito.

Passo 3 - Consentire esattamente una risorsa attendibile

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

Il metodo OnLoading del gestore imposta args.Skipped = false e args.Data su quei byte quando l’URI richiesto corrisponde, così il documento ottiene il logo senza che GroupDocs.Merger effettui alcuna richiesta.

Quando abbiamo eseguito l’unione predefinita su fattura e nota di consegna, tutti i sette riferimenti esterni nei due file sono stati bloccati – il foglio di stile remoto, il logo remoto, l’URL in stile metadati, l’immagine nella cartella sorella, un percorso file:///, un percorso UNC e un URL di tracciamento con query string. joined.html conserva il testo di entrambi gli upload; un segnaposto sostituisce ogni immagine bloccata. Con il gestore trusted-logo collegato, il logo è stato restituito come supplied e gli altri sei sono rimasti blocked.

Confronto: Prima vs. Dopo

Prima (senza opzione) Dopo (GroupDocs.Merger 26.10)
Richieste esterne Ogni riferimento veniva caricato automaticamente Negato a meno che non sia esplicitamente consentito
Percorsi assoluti a file locali Caricati nell’output Negati come qualsiasi altro riferimento
Eccezioni attendibili Non necessarie – tutto veniva caricato Una risorsa tramite gestore, o un prefisso nella whitelist
Voce di whitelist errata Nessuna whitelist esistente Rifiutata con ArgumentException al momento della costruzione
Codice richiesto Nessun modo per limitare il caricamento Poche righe su LoadOptions

Il contrasto sta in ciò che è possibile, non solo in ciò che è comodo: prima della 26.10 non esisteva alcuna impostazione che fermasse il caricamento; dopo, il rifiuto è il valore predefinito e ogni eccezione è esplicita.

Questo romperà l’HTML che già dipendeva dal caricamento di immagini remote?

Se quell’HTML viene ancora unito con le LoadOptions predefinite, sì – le immagini non verranno caricate e un segnaposto le sostituirà nell’output. La soluzione è esplicita, non un workaround: elenca le fonti attendibili in WhitelistedResources, oppure collega un IExternalResourceHandler che fornisce le risorse specifiche che desideri ancora renderizzare. In entrambi i casi l’unione continua a funzionare; semplicemente smette di recuperare tutto ciò che non è stato nominato.

Esempio reale: Consolidamento di fattura e nota di consegna

Una pipeline di documenti che unisce HTML inviato dal cliente in una copia d’archivio è esattamente lo scenario modellato dal progetto di esempio di questa pagina: un upload di fattura e una nota di consegna dello stesso ordine, uniti in un unico file. Nessuno dei due upload è stato scritto dal team della pipeline, quindi non si può presumere che sia sicuro leggerlo letteralmente. La fattura indica un importo dovuto e un cliente, Northwind Traders, e contiene un riferimento al logo, un foglio di stile di tracciamento, un URL in stile metadati cloud, un riferimento a un’immagine nella stessa cartella e un percorso file:/// – sette riferimenti esterni tra i due upload, nessuno dei quali la pipeline di archiviazione dovrebbe recuperare per conto del cliente.

Prima che il lavoro di unione venga messo in produzione, vale la pena decidere, documento per documento, se sia necessaria qualche eccezione. Lo scenario di fatturazione di questo esempio richiedeva esattamente una: il logo dell’azienda, servito da un file già presente su disco anziché recuperato dall’URL remoto indicato nell’HTML. Tutto il resto – il foglio di stile di tracciamento, l’indirizzo in stile metadati, il file fratello, i percorsi UNC e file:/// – non aveva alcuna ragione legittima per essere recuperato, quindi le impostazioni predefinite erano la risposta corretta.

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

Fornire un semplice nome host anziché un prefisso assoluto come https://cdn.example.com/brand/ genera un’eccezione, con il messaggio Whitelisted resource 'cdn.example.com' must be an absolute URI without a query, fragment or encoded '/', '\' or '.'. (Parameter 'WhitelistedResources') – prima che venga aperto anche solo un upload. Catturare una whitelist mal configurata all’avvio vale più che scoprirla dopo che una risorsa ha fallito silenziosamente il caricamento in produzione.

Cos’altro puoi fare con GroupDocs.Merger per .NET?

  • Convalidare le voci della whitelist in anticipo: una voce WhitelistedResources malformata fallisce alla costruzione del Merger, così i controlli all’avvio del deployment la intercettano prima che venga elaborato qualsiasi documento.
  • Fornire risorse attendibili da disco: IExternalResourceHandler può rispondere a una richiesta con byte già disponibili, senza alcun accesso di rete.
  • Applicare una politica unica a ogni documento unito: LoadOptions viene impostato una sola volta sul Merger, e ogni documento successivamente unito segue le stesse regole.
  • Coprire più di HTML: elaborazione testi, HTML/MHTML, presentazioni, fogli di calcolo, SVG ed EPUB rispettano tutte le stesse opzioni.

Conclusione

Unire HTML che non hai scritto non significa più fidarsi di tutto ciò che contiene. GroupDocs.Merger per .NET nega le risorse esterne per impostazione predefinita dalla versione 26.10, e i due modi per concedere un’eccezione – un gestore di risorse attendibili e WhitelistedResources – ti danno esattamente l’accesso necessario al tuo flusso, nient’altro.

Pronto a provarlo da solo?

Risorse aggiuntive