💡 Full working example available on GitHub:
scrub-office-document-pii-dotnet
A Velha Maneira Era Dolorosa
A rotina funciona assim. Abra o documento, Arquivo, Informações, Verificar Problemas, Inspecionar Documento, marque as caixas, Remover Tudo, salve com um novo nome, feche, abra o próximo. Quarenta arquivos depois alguém percebe que a Inspeção de Documento também removeu o Título que o índice de registros usa como chave, e que a cópia enviada uma hora antes ainda continha um ID de aprovador do SharePoint, porque o arquivo havia sido salvo por um aplicativo diferente que escreveu o campo de volta.
A remoção de PII de metadados é uma capacidade do GroupDocs.Metadata para .NET que exclui propriedades que contêm identidade de documentos Office programaticamente e relata o que resta depois. A rotina manual falha em três aspectos: não escala para mais de alguns arquivos, é tudo‑ou‑nada quanto aos campos que são removidos e não produz nenhum registro do que foi excluído. Este artigo mostra a versão .NET do mesmo trabalho, um grupo de propriedades por vez.
Ajuda saber o que realmente está lá. Um arquivo Word que passou por uma rodada de revisão normalmente contém Author e LastSavedBy da conta Windows de quem o salvou, Manager e Company do modelo corporativo, um contador de revisões, TotalEditingTime, um carimbo de data/hora LastPrinted e contadores de threads de comentários. Adicione o SharePoint à cadeia e você também obtém identificadores de aprovadores, caminhos de workflow, URIs de tipo de conteúdo e o modelo a partir do qual o documento foi criado. Nada disso é visível na página, e tudo viaja no mesmo arquivo.
Há Uma Maneira Melhor
Tudo no GroupDocs.Metadata para .NET passa por um único motor de busca de propriedades. RemoveProperties recebe uma lambda sobre MetadataProperty, exclui cada propriedade que a lambda aceita e devolve a contagem. FindProperties executa a mesma lambda sem escrever. As propriedades também carregam tags, de modo que Tags.Person.Creator identifica campos do tipo autor em diferentes formatos e pacotes, em vez de combinar nomes literais que variam conforme o aplicativo produtor.
Isso oferece três formas de limpeza em vez de um único botão: uma passagem por tag para campos de identidade, passagens por nome para famílias como comentários e revisões, e Sanitize() quando nada deve permanecer. Todas as três retornam números, e são esses números que tornam a passagem auditável.
A Nova Maneira: Um Predicado por Grupo de Propriedade
Etapa 1 – Limpar os nomes
Quatro verificações de tag cobrem o grupo de identidade. Campos descritivos permanecem intactos, o que difere da opção Remover Tudo do Inspetor de Documentos:
using (var metadata = new Metadata(inputPath))
{
if (metadata.FileFormat == FileFormat.Unknown) return 0;
var affected = metadata.RemoveProperties(p =>
p.Tags.Contains(Tags.Person.Creator) ||
p.Tags.Contains(Tags.Person.Editor) ||
p.Tags.Contains(Tags.Person.Manager) ||
p.Tags.Contains(Tags.Corporate.Company));
metadata.Save(outputPath);
return affected;
}
A verificação FileFormat.Unknown é a guarda que mantém um zero honesto: sem ela, um arquivo ilegível e um arquivo limpo parecem iguais para o chamador.
Etapa 2 – Limpar as famílias ao redor
Comentários, revisões e campos de servidor não têm tag, portanto o predicado combina nomes. A linha do tempo de edição é o grupo mais frequentemente esquecido, e é aquele que indica como um documento foi produzido:
using (var metadata = new Metadata(inputPath))
{
if (metadata.FileFormat == FileFormat.Unknown) return 0;
var affected = metadata.RemoveProperties(p =>
p.Name != null && (
p.Name.Contains("Revision") ||
p.Name.Contains("TrackedChange") ||
p.Name.Contains("LastPrinted") ||
p.Name.Contains("TotalEditingTime") ||
p.Name.Contains("EditTime")));
metadata.Save(outputPath);
return affected;
}
A correspondência por substring é deliberada: captura CommentsCount junto com Comment, e TotalEditingTime junto com EditTime, sem precisar manter uma lista de nomes exatos por formato. A passagem do SharePoint usa a mesma chamada com Server, Workflow, Approver, ContentType e Template.
Etapa 3 – Apagar tudo, então verificar o resultado
No limite de confiança, uma única chamada substitui as quatro passagens:
using (var metadata = new Metadata(inputPath))
{
if (metadata.FileFormat == FileFormat.Unknown) return 0;
var affected = metadata.Sanitize();
metadata.Save(outputPath);
return affected;
}
Então vem a parte que a rotina manual não tem equivalente. A varredura de verificação reutiliza os predicados de remoção através de FindProperties e classifica os sobreviventes em duas listas:
foreach (var p in props)
{
var value = p.InterpretedValue?.ToString() ?? p.Value?.ToString() ?? string.Empty;
if (string.IsNullOrWhiteSpace(value)) continue;
if (value == "0" || value == "0.0") continue;
var entry = $"{p.Name}={value}";
var name = p.Name ?? string.Empty;
if (name.StartsWith("Comment") || name.StartsWith("Revision"))
report.ContentLevelLeaks.Add(entry);
else
report.MetadataLeaks.Add(entry);
}
MetadataLeaks deve estar vazio antes que um arquivo seja considerado sanitizado. ContentLevelLeaks é informativo: comentários do Word e autores de alterações rastreadas ficam em word/document.xml, que é conteúdo do corpo, e limpar esses itens requer uma biblioteca de edição de conteúdo como Aspose.Words, e não uma API de metadados.
Por que não simplesmente chamar Sanitize em tudo?
Porque a maioria dos documentos ainda está em uso. Sanitize() limpa cada pacote detectado, e isso inclui Title, Subject e Keywords, campos dos quais um sistema de registros e um índice de busca dependem. Use as passagens direcionadas enquanto o arquivo circula internamente, mantenha os metadados descritivos funcionando e reserve a limpeza total para a cópia que realmente sai da organização.
Lado a Lado: Antes vs. Depois
| Inspeção manual | GroupDocs.Metadata para .NET | |
|---|---|---|
| Seletividade | Remover Tudo, campos descritivos incluídos | um predicado por grupo de propriedade |
| Cobertura | campos expostos na caixa de diálogo | todos os pacotes que a biblioteca detecta, partes OOXML personalizadas incluídas |
| Registro | nenhum | contagem de afetados retornada por operação |
| Verificação | reabrir e observar | varredura FindProperties com listas de metadados e nível de conteúdo |
| Lote de 200 arquivos | 200 cliques | um loop, cinco operações, uma linha de log por arquivo |
A linha que altera o comportamento é o registro. Quando cada passagem devolve uma contagem, a sanitização deixa de ser um passo que alguém lembra de fazer e passa a ser um dado que o pipeline pode validar: um limiar em um teste, um campo em uma tabela de auditoria, uma condição que falha um job noturno. Essa também é a linha que uma rotina manual não consegue produzir em nenhum nível de disciplina.
Exemplo do Mundo Real: O Gancho Pré-Envio
Um portal de suporte permite que a equipe anexe documentos a tickets de cliente. O manipulador de anexos agora executa a passagem de identidade e a passagem de servidor antes de armazenar o arquivo, registra ambas as contagens no ticket e executa a verificação de vazamento na cópia salva. Uma lista de vazamento de metadados não vazia rejeita o upload com uma mensagem nomeando a propriedade ofensiva, de modo que a pessoa que está anexando o arquivo descobre imediatamente em vez de depois que ele chega ao cliente.
Dois detalhes tornam esse gancho prático. As passagens gravam em um novo caminho, então o original permanece no armazenamento do colaborador e nada é destruído por um passo automatizado. E as contagens vão para o registro do ticket ao lado do anexo, o que significa que a resposta a “o que foi removido deste documento” é um número armazenado, e não uma suposição sobre o que o pipeline costuma fazer.
A primeira vez que apontei essa verificação para um modelo real, ela retornou um valor Manager que a passagem de identidade havia removido segundos antes e que o modelo corporativo havia escrito de volta ao salvar. A chamada de remoção estava funcionando exatamente como documentado; o problema estava no pipeline ao redor, e só a leitura de volta mostrou isso.
O Que Mais Você Pode Fazer com GroupDocs.Metadata?
O mesmo motor de predicados lê. Comparar propriedades entre duas versões de um documento revela mudanças de propriedade e reautoria fora do processo de revisão, e a visão geral de limpeza de metadados cobre onde uma ferramenta interativa ainda se encaixa ao lado de uma passagem orientada por API. Como o sistema de tags abrange formatos, o predicado de identidade escrito aqui também funciona contra PDFs, imagens e arquivos de áudio sem modificação.
Essa portabilidade vale a pena planejar. Uma regra de limpeza escrita como lambda sobre MetadataProperty é C# comum, então pode viver em uma biblioteca compartilhada, ser testada unitariamente contra documentos de exemplo e ser aplicada por qualquer serviço que precise: um endpoint de exportação, um job programado de registros ou um passo de build que sanitiza anexos de documentação antes do lançamento. As regras permanecem em um único lugar; apenas os pontos de chamada mudam.
Conclusão
Quatro passagens direcionadas, uma sanitização completa, uma varredura de verificação. Esse conjunto cobre o espectro prático para documentos Office: mantenha os metadados descritivos enquanto o arquivo circula, limpe tudo quando ele sai, e comprove o resultado de qualquer forma. Clone o exemplo, execute-o contra um documento que passou por uma rodada real de revisão e leia as contagens de afetados. Elas costumam ser maiores que o esperado, que é exatamente o objetivo.