💡 Exemple complet fonctionnel disponible sur GitHub :
skip-external-resources-when-signing-dotnet
Introduction
Un document Word peut contenir une image qui n’est pas intégrée au fichier. Le document conserve une adresse, et tout ce qui l’ouvre récupère cette adresse. Sur un ordinateur de bureau, c’est une fonctionnalité — l’image se met à jour lorsque la source le fait. Sur un serveur qui accepte des téléchargements, cela signifie que la personne qui vous a envoyé le fichier décide quelles URL votre infrastructure va demander.
Le chargement sécurisé de documents est un comportement de GroupDocs.Signature pour .NET qui refuse de faire ces requêtes. Depuis la version 26.9, LoadOptions.SkipExternalResources vaut true par défaut. Cet article compare les trois modes de chargement sur le même document, montre comment autoriser un hôte sans autoriser tous les hôtes, et explique pourquoi la signature d’un fichier non fiable ne nécessite aucun accès réseau.
Pourquoi cela importe plus qu’il n’y paraît
L’attaque porte un nom — server‑side request forgery (SSRF) — et se présente sous trois formes concrètes.
Une adresse interne inaccessible depuis Internet l’est depuis votre serveur, de sorte qu’un document manipulé peut faire récupérer par votre service http://169.254.169.254/ ou un point de terminaison d’administration sur localhost et, selon ce que vous faites du résultat, le divulguer. Un chemin UNC dans un document peut inciter un hôte Windows à s’authentifier en sortie, remettant ainsi des identifiants à un serveur contrôlé par l’attaquant. Et un lien vers un hôte qui ne répond jamais bloque le fil de chargement jusqu’à l’expiration, ce qui est un moyen économique d’épuiser un pool de travailleurs.
J’avais supposé qu’il s’agissait d’une préoccupation théorique jusqu’à ce que je voie un document de test récupérer une image via un service qui n’avait aucune raison de faire des requêtes sortantes. Aucun de ces scénarios n’exige un bug dans la bibliothèque de documents. Suivre un lien est ce que le format demande ; la question est seulement de savoir si votre serveur doit s’y conformer.
Méthode 1 — Le nouveau comportement par défaut
Aucun LoadOptions :
using var signature = new Signature(sourcePath);
return SavePagePreview(signature, previewPath);
Rien n’est récupéré. L’aperçu affiche un espace réservé vide à la place de l’image liée, et le PNG est plus petit qu’il ne le serait autrement. Cette différence de taille est la preuve la plus pratique qu’aucune requête n’a quitté la machine.
Quelles fonctionnalités sont considérées comme externes ? Les images liées plutôt que les images intégrées, les champs INCLUDEPICTURE, les images liées dans les présentations et les feuilles de calcul, ainsi que les images et feuilles de style référencées par un SVG. Le contenu intégré reste intact — il est déjà présent dans le fichier.
Méthode 2 — Liste blanche d’une adresse
De nombreux documents pointent vers des sources légitimes : un CDN d’entreprise, un serveur d’images interne, un magasin de modèles. Autorisez cela et rien d’autre :
var loadOptions = new LoadOptions
{
WhitelistedResources = new List<string> { trustedAddress }
};
using var signature = new Signature(sourcePath, loadOptions);
La règle de correspondance mérite de l’attention. Il s’agit d’un test de sous‑chaîne insensible à la casse sur l’adresse de la ressource, ce qui signifie qu’un fragment court est dangereux : github correspond à github.attacker.example/payload.png aussi facilement que l’hôte que vous aviez prévu. Utilisez un schéma, un hôte et un chemin — l’exemple de liste blanche utilise raw.githubusercontent.com/groupdocs-signature/.
Méthode 3 — Tout autoriser
Le comportement pré‑26.9, toujours disponible :
var loadOptions = new LoadOptions { SkipExternalResources = false };
Raisonnable pour les documents générés par votre propre application. Un piège à signaler : la propriété obsolète LoadExternalResources a la polarité opposée, donc SkipExternalResources = false remplace LoadExternalResources = true. Copier une valeur depuis l’ancienne propriété inverse votre posture de sécurité sans aucune alerte.
Comparaison des trois modes : quand les utiliser
| Mode | Idéal pour | Principaux avantages | Limitations |
|---|---|---|---|
| Par défaut (skip) | téléchargements d’utilisateurs, e‑mail, fichiers partenaires | aucune requête sortante n’est possible | les images liées s’affichent comme des espaces réservés |
| Liste blanche | documents qui pointent vers un hôte que vous possédez | les liens légitimes restent fonctionnels | la correspondance par sous‑chaîne nécessite un fragment long et spécifique |
| Tout autoriser | fichiers générés par vos propres systèmes | les aperçus sont exactement comme avant | rétablit l’exposition SSRF que le comportement par défaut a supprimée |
La signature a‑t‑elle besoin de ces ressources ?
Non, et c’est le gain pratique. Une signature QR‑code est appliquée avec les paramètres de chargement par défaut et aucune ressource externe n’est demandée pendant le chargement, la signature ou l’enregistrement du document :
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);
Le document signé conserve son lien, de sorte qu’un utilisateur qui ouvre le document plus tard voit toujours l’image résolue sur sa propre machine. Le saut est une politique côté serveur, pas une modification du document — c’est ce qui le rend sûr à appliquer aux fichiers que vous traitez pour le compte de tiers.
Ce qui change lors d’une mise à jour
Pour la plupart des services, rien de visible à première vue, et il est utile de le dire clairement parce qu’un paramètre de sécurité qui modifie le comportement partout ne survivrait pas à une révision de mise à jour. L’exception se situe chaque fois qu’un aperçu ou une vignette affichait auparavant une image liée et montre maintenant un espace réservé ; c’est le changement qui fait son travail, et la solution consiste à ajouter une entrée de liste blanche si l’hôte vous appartient, ou à accepter le comportement si le document provient de l’extérieur.
La façon honnête de vérifier est celle utilisée dans l’exemple : rendre le même document sous les trois modes et comparer les tailles de sortie. Si les aperçus par défaut et ceux en liste blanche ont la même taille, aucune ressource n’a été récupérée dans les deux cas — ce qui signifie généralement que l’hôte est inaccessible depuis cette machine plutôt que que la liste blanche ait échoué, et l’exemple affiche un indice indiquant exactement cela.
L’assistant d’aperçu, puisqu’il n’est pas évident
Deux des trois modes ci‑dessus appellent un petit assistant, et il vaut la peine de le montrer parce que PreviewOptions ne prend pas de chemin :
var previewOptions = new PreviewOptions(
pageData => File.Create(previewPath),
(pageData, pageStream) => pageStream.Dispose())
{
PreviewFormat = PreviewOptions.PreviewFormats.PNG
};
signature.GeneratePreview(previewOptions);
Il accepte deux usines de flux — une pour créer un flux par page, une autre pour le libérer. Le document d’exemple possède une seule page, donc un fichier est écrit ; pour une entrée multi‑pages, ajoutez le numéro de page au nom du fichier ou chaque page écrasera la précédente.
Bonnes pratiques
- Considérez tout ce que vous n’avez pas généré comme non fiable, y compris les fichiers provenant de partenaires disposant d’une bonne posture de sécurité.
- Faites en sorte que les fragments de liste blanche soient suffisamment longs pour être sans ambiguïté, et revoyez‑les lorsqu’un CDN change.
- Ne définissez jamais
SkipExternalResourcesà partir d’une valeur qui était assignée àLoadExternalResources. - Vérifiez avec les tailles de sortie plutôt qu’avec le paramètre ; une configuration qui semble correcte et une requête qui ne s’est pas produite sont deux affirmations différentes.
Où cela en est pour le SVG
Il vaut la peine de le souligner séparément, car le SVG est à la fois un format d’upload courant et un vecteur SSRF fréquent. Un SVG peut référencer des images et des feuilles de style par URL, et ces références sont des ressources externes soumises à la même règle — ignorées par défaut, pouvant être ajoutées à une liste blanche, restaurables. Un service qui accepte des avatars ou des logos SVG et les rend côté serveur était exactement le type de système que ce changement protège.
Si votre pipeline accepte des SVG provenant d’utilisateurs, le paramètre par défaut est celui que vous voulez, et la liste blanche ne sert que dans le cas où vos propres modèles récupèrent une feuille de style partagée depuis un hôte que vous gérez.
Conclusion
Le comportement par défaut a été inversé afin que le comportement à risque nécessite une décision explicite et que le comportement sûr ne nécessite rien. Conservez le paramètre par défaut pour les entrées non fiables, utilisez une liste blanche restreinte lorsque vos propres hôtes sont impliqués, et souvenez‑vous que la signature elle‑même n’a jamais eu besoin du réseau. Exécuter l’exemple sur l’un de vos propres documents prend une minute et vous indique, en trois tailles de fichiers, exactement ce que votre service a récupéré.