💡 Voll funktionsfähiges Beispiel auf GitHub verfügbar:
scrub-office-document-pii-dotnet
Der alte Weg war schmerzhaft
Die Routine läuft so ab: Dokument öffnen, Datei → Info → Auf Probleme prüfen → Dokument prüfen, die Kästchen ankreuzen, Alles entfernen, unter neuem Namen speichern, schließen, das nächste öffnen. Nach vierzig Dateien bemerkt jemand, dass „Dokument prüfen“ auch den Titel entfernt hat, auf dem die Registerschlüssel des Archivs basieren, und dass die vor einer Stunde versendete Kopie noch eine SharePoint‑Genehmiger‑ID enthielt, weil die Datei von einer anderen Anwendung gespeichert wurde, die das Feld wieder zurückschrieb.
Metadata‑PII‑Entfernung ist eine GroupDocs.Metadata‑Funktion für .NET, die identitätsbezogene Eigenschaften aus Office‑Dokumenten programmgesteuert löscht und anschließend berichtet, was noch übrig ist. Das manuelle Vorgehen scheitert aus drei Gründen: Es skaliert nicht über ein paar Dateien hinaus, es ist ein Alles‑oder‑Nichts‑Ansatz, welche Felder entfernt werden, und es erzeugt keinen Nachweis darüber, was entfernt wurde. Dieser Artikel zeigt die .NET‑Version derselben Arbeit, eine Eigenschaftsgruppe nach der anderen.
Es hilft zu wissen, was tatsächlich darin steckt. Eine Word‑Datei, die einen Review‑Durchlauf hinter sich hat, enthält typischerweise Author und LastSavedBy vom Windows‑Konto desjenigen, der sie gespeichert hat, Manager und Company aus der Unternehmensvorlage, einen Revisionszähler, TotalEditingTime, einen LastPrinted‑Zeitstempel und Zähler für Kommentar‑Threads. Fügt man SharePoint hinzu, erhält man außerdem Genehmiger‑Kennungen, Workflow‑Pfade, Content‑Type‑URIs und die Vorlage, aus der das Dokument erstellt wurde. Keines davon ist auf der Seite sichtbar, und alles reist in derselben Datei mit.
Es gibt einen besseren Weg
Alles in GroupDocs.Metadata für .NET läuft über eine einzige Eigenschaftssuchmaschine. RemoveProperties nimmt ein Lambda über MetadataProperty, löscht jede Eigenschaft, die das Lambda akzeptiert, und gibt die Anzahl zurück. FindProperties führt dasselbe Lambda aus, ohne zu schreiben. Eigenschaften tragen außerdem Tags, sodass Tags.Person.Creator autor‑ähnliche Felder über Formate und Pakete hinweg identifiziert, anstatt wörtliche Namen zu vergleichen, die je nach erzeugender Anwendung variieren.
Damit ergeben sich drei Formen der Bereinigung statt eines einzigen Knopfes: ein Tag‑Durchlauf für Identitätsfelder, Namens‑Durchläufe für Familien wie Kommentare und Revisionen und Sanitize(), wenn nichts überleben soll. Alle drei geben Zahlen zurück, und genau diese Zahlen machen den Durchlauf prüfbar.
Der neue Weg: Ein Prädikat pro Eigenschaftsgruppe
Schritt 1 – Namen leeren
Vier Tag‑Prüfungen decken die Identitätsgruppe ab. Beschreibende Felder bleiben unangetastet, was den Unterschied zum „Alle entfernen“ des Document Inspectors ausmacht:
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;
}
Die Prüfung FileFormat.Unknown ist die Absicherung, die eine Null‑Rückgabe ehrlich hält: Ohne sie sehen eine nicht lesbare Datei und eine saubere Datei für den Aufrufer gleich aus.
Schritt 2 – Familien um sie herum leeren
Kommentare, Revisionen und Server‑Felder besitzen keinen Tag, daher stimmt das Prädikat Namen ab. Der Bearbeitungs‑Zeitstrahl ist die Gruppe, die am häufigsten vergessen wird, und zugleich die, die aussagt, wie ein Dokument erzeugt wurde:
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;
}
Das Teilstring‑Matching ist bewusst gewählt: Es erfasst CommentsCount neben Comment und TotalEditingTime neben EditTime, ohne für jedes Format eine exakte Namensliste zu pflegen. Der SharePoint‑Durchlauf ist derselbe Aufruf mit Server, Workflow, Approver, ContentType und Template.
Schritt 3 – Alles löschen, dann Ergebnis prüfen
An der Vertrauensgrenze ersetzt ein Aufruf die vier Durchläufe:
using (var metadata = new Metadata(inputPath))
{
if (metadata.FileFormat == FileFormat.Unknown) return 0;
var affected = metadata.Sanitize();
metadata.Save(outputPath);
return affected;
}
Dann folgt der Teil, für den das manuelle Verfahren kein Gegenstück hat. Der Verifikations‑Scan nutzt die Entferungs‑Prädikate erneut über FindProperties und sortiert die Überlebenden in zwei Listen:
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 muss leer sein, damit eine Datei als gesäubert gilt. ContentLevelLeaks ist rein informativ: Word‑Kommentare und Autoren von nachverfolgten Änderungen liegen in word/document.xml, also im Body‑Inhalt, und deren Entfernung erfordert eine Content‑Editing‑Bibliothek wie Aspose.Words statt einer reinen Metadata‑API.
Warum nicht einfach überall Sanitize() aufrufen?
Weil die meisten Dokumente noch in Gebrauch sind. Sanitize() löscht jedes erkannte Paket, darunter Titel, Betreff und Schlüsselwörter – Felder, von denen ein Records‑System und ein Such‑Index abhängen. Verwenden Sie die gezielten Durchläufe, solange eine Datei intern zirkuliert, lassen Sie die beschreibenden Metadaten funktionieren und reservieren Sie das komplette Löschen für die Kopie, die das Unternehmen verlässt.
Gegenüberstellung: Vorher vs. Nachher
| Manuelle Inspektion | GroupDocs.Metadata für .NET | |
|---|---|---|
| Selektivität | Alles entfernen, beschreibende Felder inkl. | ein Prädikat pro Eigenschaftsgruppe |
| Abdeckung | nur die im Dialog sichtbaren Felder | jedes vom Bibliothek erkannte Paket, inkl. benutzerdefinierter OOXML‑Teile |
| Protokollierung | keine | betroffene Anzahl wird pro Vorgang zurückgegeben |
| Verifikation | erneut öffnen und schauen | FindProperties‑Scan mit Metadaten‑ und Content‑Level‑Listen |
| Stapel von 200 Dateien | 200 Klick‑Durchläufe | ein Durchlauf, fünf Vorgänge, eine Log‑Zeile pro Datei |
Die Zeile, die das Verhalten ändert, ist die Protokollierung. Sobald jeder Durchlauf eine Zahl zurückgibt, hört die Säuberung auf, ein Schritt zu sein, den jemand „vergisst“, und wird zu Daten, die die Pipeline prüfen kann: ein Schwellenwert in einem Test, ein Feld in einer Audit‑Tabelle, eine Bedingung, die einen nächtlichen Job fehlschlagen lässt. Das ist ebenfalls die Zeile, die ein manuelles Verfahren niemals auf irgendeinem Disziplin‑Level erzeugen kann.
Praxisbeispiel: Der Pre‑Send‑Hook
Ein Support‑Portal lässt Mitarbeitende Dokumente an Kundentickets anhängen. Der Anhang‑Handler führt nun den Identitäts‑Durchlauf und den Server‑Durchlauf aus, bevor die Datei gespeichert wird, protokolliert beide Zähler im Ticket und führt die Leak‑Prüfung auf der gespeicherten Kopie durch. Eine nicht‑leere Metadaten‑Leak‑Liste verwirft den Upload mit einer Meldung, die die fehlerhafte Eigenschaft nennt, sodass die Person, die die Datei anhängt, sofort erfährt, warum der Upload abgelehnt wurde, anstatt erst nach dem Versand an den Kunden.
Zwei Details machen diesen Hook praktisch: Die Durchläufe schreiben in einen neuen Pfad, sodass das Original im Speicher des Mitarbeitenden bleibt und nichts durch einen automatisierten Schritt zerstört wird. Und die Zähler werden im Ticket‑Datensatz neben dem Anhang abgelegt, was bedeutet, dass die Antwort auf „Was wurde aus diesem Dokument entfernt?“ eine gespeicherte Zahl ist und nicht eine Annahme darüber, was die Pipeline normalerweise tut.
Als ich das erste Mal diesen Check an einer echten Vorlage anwandte, kam ein Manager‑Wert zurück, den der Identitäts‑Durchlauf Sekunden zuvor entfernt hatte und die Unternehmensvorlage beim Speichern sofort wieder zurückschrieb. Der Entferungs‑Aufruf funktionierte exakt wie dokumentiert; das Problem lag in der umgebenden Pipeline, und nur das erneute Auslesen zeigte es.
Was lässt sich sonst noch mit GroupDocs.Metadata tun?
Die gleiche Prädikat‑Engine liest. Der Vergleich von Eigenschaften zwischen zwei Versionen eines Dokuments deckt Eigentumswechsel und erneutes Autorisieren außerhalb des Review‑Prozesses auf, und das metadata scrubbing overview erklärt, wo ein interaktives Tool neben einem API‑gesteuerten Durchlauf noch Sinn macht. Da das Tag‑System Formate übergreifend ist, läuft das hier geschriebene Identitäts‑Prädikat ebenfalls gegen PDFs, Bilder und Audiodateien, ohne Modifikation.
Diese Portabilität lohnt sich bei der Planung. Eine Bereinigungs‑Regel, geschrieben als Lambda über MetadataProperty, ist gewöhnliches C#, kann also in einer gemeinsamen Bibliothek leben, gegen Fixture‑Dokumente unit‑getestet werden und von jedem Service verwendet werden, der sie benötigt: ein Export‑Endpoint, ein geplanter Records‑Job oder ein Build‑Schritt, der Dokumentations‑Anhänge vor dem Release säubert. Die Regeln bleiben an einem Ort; nur die Aufrufstellen ändern sich.
Fazit
Vier gezielte Durchläufe, ein vollständiges Sanitize(), ein Verifikations‑Scan. Dieses Set deckt den praktischen Anwendungsbereich für Office‑Dokumente ab: beschreibende Metadaten während der internen Zirkulation behalten, alles entfernen, wenn das Dokument das Unternehmen verlässt, und das Ergebnis in jedem Fall nachweisen. Klonen Sie das Beispiel, führen Sie es gegen ein Dokument aus, das einen echten Review‑Durchlauf hinter sich hat, und lesen Sie die betroffenen Zähler. Sie sind meist höher als erwartet – genau das ist der Sinn.