💡 Exemplo completo em funcionamento disponível no GitHub:
exemplo completo

Introdução

Um documento Word pode conter uma imagem que não está no arquivo. O documento guarda um endereço, e quem o abre busca esse endereço. Em um desktop isso é um recurso – a imagem é atualizada quando a fonte muda. Em um servidor que aceita uploads, isso significa que a pessoa que enviou o arquivo decide quais URLs sua infraestrutura solicita.

O carregamento seguro de documentos é um comportamento do GroupDocs.Signature para .NET que recusa fazer essas solicitações. A partir da versão 26.9, LoadOptions.SkipExternalResources tem o valor padrão true. Este artigo compara os três modos de carregamento usando o mesmo documento, mostra como permitir um host sem permitir todos eles e explica por que assinar um arquivo não confiável não precisa de acesso à rede.

Por que isso importa mais do que parece

O ataque tem um nome – server‑side request forgery (SSRF) – e três formas concretas.

Um endereço interno que não é acessível pela internet pode ser alcançado a partir do seu servidor, de modo que um documento mal‑formado pode fazer seu serviço buscar http://169.254.169.254/ ou um ponto de extremidade de administração no localhost e, dependendo do que você faz com o resultado, vazar essas informações. Um caminho UNC em um documento pode fazer um host Windows autenticar externamente, entregando credenciais a um servidor controlado pelo atacante. E um link para um host que simplesmente nunca responde prende a thread de carregamento até que ocorra timeout, sendo uma maneira barata de esgotar um pool de workers.

Eu supus que isso era apenas uma preocupação teórica até observar um documento de teste puxar uma imagem por meio de um serviço que não deveria fazer requisições externas de forma alguma. Nada disso requer um bug na biblioteca de documentos. Seguir um link é o que o formato pede; a questão é apenas se o seu servidor deve obedecer.

Método 1 - O Novo Padrão

Sem LoadOptions nenhum:

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

Nada é buscado. A pré‑visualização renderiza um espaço reservado vazio onde a imagem vinculada estaria, e o PNG fica menor do que seria caso contrário. Essa diferença de tamanho é a prova mais prática de que nenhuma requisição saiu da máquina.

Quais recursos contam como externos? Imagens vinculadas em vez de incorporadas, campos INCLUDEPICTURE, imagens vinculadas em apresentações e planilhas, e as imagens e folhas de estilo que um SVG referencia. Conteúdo incorporado permanece intacto – já está no arquivo.

Método 2 - Lista Branca de Um Endereço

Muitos documentos vinculam a algo legítimo: um CDN da empresa, um servidor interno de imagens, uma loja de modelos. Permita isso e nada mais:

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

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

A regra de correspondência merece atenção. Ela faz um teste de substring sem diferenciar maiúsculas de minúsculas contra o endereço do recurso, o que significa que um fragmento curto é perigoso: github corresponde a github.attacker.example/payload.png tão facilmente quanto ao host que você pretendia. Use um esquema, um host e um caminho – o exemplo inclui na lista branca raw.githubusercontent.com/groupdocs-signature/.

Método 3 - Permitir Tudo

O comportamento pré‑26.9, ainda disponível:

var loadOptions = new LoadOptions { SkipExternalResources = false };

Razoável para documentos produzidos pela sua própria aplicação. Uma armadilha que vale a pena sinalizar: a propriedade obsoleta LoadExternalResources tem polaridade oposta, portanto SkipExternalResources = false substitui LoadExternalResources = true. Copiar um valor da propriedade antiga inverte sua postura de segurança sem gerar erro algum.

Comparando os Três: Quando Usar Cada

Modo Melhor Para Principais Vantagens Limitações
Padrão (pular) uploads de usuários, e‑mail, arquivos de parceiros nenhuma requisição externa é possível imagens vinculadas são exibidas como espaços reservados
Lista Branca documentos que vinculam a um host que você controla mantém links legítimos funcionando a correspondência por substring requer um fragmento longo e específico
Permitir tudo arquivos gerados pelos seus próprios sistemas as pré‑visualizações ficam exatamente como antes restaura a exposição ao SSRF que o padrão removeu

E quanto à assinatura – isso precisa dos recursos?

Não, e esse é o ganho prático. Uma assinatura de código QR é aplicada com as configurações padrão de carregamento e nenhum recurso externo é solicitado enquanto o documento é carregado, assinado ou salvo:

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

A saída assinada mantém seu link, de modo que um usuário que abrir o documento mais tarde ainda verá a imagem resolvida na própria máquina. Pular é uma política do lado do servidor, não uma edição do documento – e é isso que o torna seguro para aplicar a arquivos que você manipula em nome de terceiros.

O Que Muda ao Atualizar

Para a maioria dos serviços, nada visível à primeira vista, e isso vale a pena afirmar claramente porque uma mudança de padrão de segurança que altera o comportamento em todo lugar não sobreviveria a uma revisão de atualização. A exceção ocorre onde uma pré‑visualização ou miniatura costumava mostrar uma imagem vinculada e agora mostra um espaço reservado; essa é a mudança cumprindo seu papel, e a correção é uma entrada na lista branca se o host for seu, ou a aceitação se o documento vier de fora.

A forma mais honesta de verificar é a que o exemplo usa: renderizar o mesmo documento nos três modos e comparar os tamanhos de saída. Se as pré‑visualizações padrão e com lista branca forem idênticas em tamanho, nada foi buscado em nenhum dos casos – o que geralmente significa que o host está inacessível a partir daquela máquina, e não que a lista branca falhou, e o exemplo imprime uma dica dizendo exatamente isso.

O Auxiliar de Pré‑visualização, Já que Não é Óbvio

Dois dos três modos acima chamam um pequeno auxiliar, e vale a pena mostrá‑lo porque PreviewOptions não aceita um caminho:

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

signature.GeneratePreview(previewOptions);

Ele recebe duas fábricas de stream – uma para criar um stream por página, outra para liberá‑lo. O documento de exemplo tem uma única página, então um arquivo é escrito; para entrada com várias páginas, inclua o número da página no nome do arquivo ou cada página sobrescreverá a anterior.

Melhores Práticas

  • Considere tudo o que você não gerou como não confiável, incluindo arquivos de parceiros com boas posturas de segurança.
  • Torne os fragmentos da lista branca longos o suficiente para serem inequívocos e revise‑os quando um CDN mudar.
  • Nunca defina SkipExternalResources a partir de um valor que antes era atribuído a LoadExternalResources.
  • Verifique com os tamanhos de saída em vez de apenas com a configuração; uma configuração que parece correta e uma requisição que não ocorreu são afirmações diferentes.

Onde Isso Deixa o SVG

Vale destacar separadamente, porque SVG é tanto um formato de upload comum quanto um vetor frequente de SSRF. Um SVG pode referenciar imagens e folhas de estilo por URL, e essas referências são recursos externos sob a mesma regra – ignorados por padrão, passíveis de lista branca, restauráveis. Um serviço que aceita avatares ou logotipos em SVG e os renderiza no servidor era exatamente o tipo de sistema que essa mudança protege.

Se seu pipeline aceita SVG de usuários, o padrão é a configuração que você deseja, e a lista branca serve para o caso em que seus próprios modelos puxam uma folha de estilo compartilhada de um host que você controla.

Conclusão

O padrão foi invertido de modo que o comportamento arriscado requer uma decisão explícita e o seguro não precisa de nada. Mantenha o padrão para entrada não confiável, use lista branca de forma restrita onde seus próprios hosts estejam envolvidos e lembre‑se de que a própria assinatura nunca precisou da rede. Executar o exemplo contra um dos seus próprios documentos leva um minuto e informa, em três tamanhos de arquivo, exatamente o que seu serviço tem buscado.

Recursos Adicionais