💡 Volledig werkend voorbeeld beschikbaar op GitHub:
sanitize-office-document-pii-python

De gegevens die niemand controleert voordat hij op Verzenden drukt

Een kwartaalrapport voor de raad wordt naar een externe auditor gestuurd. De tekst is vlekkeloos; drie beoordelingscycli hebben dat verzekerd. Het bestand zelf vertelt een ander verhaal. De eigenschappen vermelden nog steeds de analist die het heeft opgesteld, de manager die het heeft herwerkt, de dochteronderneming die het sjabloon bezit, een LastPrinted‑tijdstempel van de avond vóór de deadline, en een SharePoint‑goedkeurder‑ID uit de interne ondertekeningsworkflow. Niets hiervan verschijnt op een pagina. Alles reist mee met het bestand.

PII‑verwijdering is een GroupDocs.Metadata‑workflow voor Python via .NET die deze identiteitsdragende eigenschappen van Word-, Excel- en PowerPoint‑bestanden programmatically verwijdert. Dit artikel vergelijkt de drie benaderingen die de API biedt: tag‑gedreven verwijdering voor identiteitsvelden, naam‑patroonverwijdering voor eigenschapsfamilies zoals opmerkingen en revisies, en de één‑oproep sanitize() die alles opruimt. Je ziet ook de stap die de meeste sanitisatiescripts overslaan, een verificatiescan die bewijst dat de opruiming daadwerkelijk heeft plaatsgevonden.

Waarom metadata‑PII een eigen pijplijn verdient

Inhouds‑reviewtools controleren wat mensen lezen. Ze controleren niet wat bestandssystemen opslaan, en die kloof is waar compliance‑incidenten ontstaan. Een GDPR‑verzoek omvat persoonsgegevens in de velden Author en Manager net zo goed als gegevens in de tekst. Juridische ontdekking leest revisietellers en bewerkingstijdtotalen om te reconstrueren hoe lang een position paper werd onderhandeld. Aanbestedings‑reviewers kunnen je organisatiestructuur afleiden uit SharePoint‑workflow‑eigenschappen, en de commentaarvelden van een persbericht bewaren de namen van reviewers naast concept‑opmerkingen. Elk daarvan is een bevinding. Geen van hen is zichtbaar in de documentinhoud.

Voorvereisten

Zorg ervoor dat je het volgende hebt voordat je begint:

  • Python 3 met pip
  • GroupDocs.Metadata voor Python via .NET, vastgezet in de voorbeeld‑repository op versie 26.5
  • Een Office‑bestand met echte eigenschappen om op te oefenen

Installatie

pip install groupdocs-metadata-net==26.5

De companion repository levert een voorbeeld‑DOCX en voert elk fragment hieronder uit als een geverifieerde pijplijn.

Methode 1: Tag‑gedreven identiteitsverwijdering

De vier meest gevoelige velden, Author, LastSavedBy, Manager en Company, hebben verschillende interne namen in Office‑formaten. Het tagsysteem lost dit op: in plaats van eigenschappen te benoemen, vraagt de predicaat om alles dat gemarkeerd is als een persoon of een bedrijf.

# Match identity properties by meaning, not by format-specific name
with Metadata("board-report.docx") as metadata:
    removed = metadata.remove_properties(lambda p:
        Tags.person.creator in list(p.tags)     # Author, LastSavedBy
        or Tags.person.editor in list(p.tags)
        or Tags.person.manager in list(p.tags)
        or Tags.corporate.company in list(p.tags))
    metadata.save("board-report-clean.docx")

print(f"{removed} identity properties removed")

Belangrijke punten:

  • Formaatonafhankelijkheid: dezelfde lambda reinigt DOCX, XLSX en PPTX omdat tags klassificeren op rol.
  • Telbaar resultaat: remove_properties geeft terug hoeveel eigenschappen overeenkwamen, wat in je auditlog moet worden opgenomen.
  • Kopie‑semantiek: opslaan naar een nieuw pad behoudt het origineel voor je administratie.

💡 Tip: deze stap behoudt Title, Subject en andere beschrijvende velden, zodat het bestand vriendelijk blijft voor zoeken en DMS‑indexering.

Methode 2: Naam‑patroonverwijdering voor eigenschapsfamilies

Tags dekken geclassificeerde concepten. Hele families van lekgevoelige velden zitten buiten die classificatie: commentaareigenschappen, revisietellers, SharePoint‑workflow‑stempels. Voor deze match je op de eigenschapsnaam zelf.

# Comment fields often live in custom properties the tag system
# does not classify, so match them by name substring
with Metadata("board-report.docx") as metadata:
    removed = metadata.remove_properties(lambda p:
        p.name is not None and (
            "Comment" in p.name
            or "Reviewer" in p.name
            or "Reviewed" in p.name))
    metadata.save("board-report-no-comments.docx")

Dezelfde structuur behandelt de andere twee families; alleen de lijst met substrings verandert:

Familie Substrings om te matchen
Revisietracé Revision, TrackedChange, LastPrinted, TotalEditingTime, EditTime
Server / workflow Server, Workflow, Approver, ContentType, Template

Dit ruilt precisie in voor bereik: "Comment" vangt ook Comments en CommentCount, wat meestal is wat een sanitisatie‑pass wil. Brede substrings kunnen onschuldige sjabloonvelden ook treffen, dus controleer het geretourneerde aantal tegen je verwachtingen.

💡 Tip: voer elke familie als een eigen pass uit wanneer je auditlog per‑categorie‑aantallen nodig heeft; voeg de substrings samen in één predicaat wanneer dat niet nodig is.

Methode 3: De één‑oproep volledige sanitatie

Wanneer het bestand de organisatie verlaat en niets in de metadata‑laag mag overleven, stop dan met het schrijven van predicaten.

# One call, every detected metadata package
with Metadata("board-report.docx") as metadata:
    removed = metadata.sanitize()
    metadata.save("board-report-final.docx")

print(f"sanitize() removed {removed} properties")

sanitize() wist elk pakket dat de bibliotheek detecteert: document‑info‑identiteitsvelden, opmerkingen, revisiegeschiedenis, auteurs van tracked changes en aangepaste OOXML‑onderdelen. Het gedrag wordt gedocumenteerd op de pagina Clean metadata. De sterkte is ook de kost. Title en Subject verdwijnen samen met de PII, waardoor het beter geschikt is voor de export‑poort dan voor het midden van een samenwerkingsworkflow.

Heb ik alle vier gerichte passes nodig?

Nee. Elke pass bestaat omdat een ander team het risico bezit. Identiteitsvelden storen privacy‑officieren, commentaarsporen storen juridische, revisietellers storen onderhandelaren, en server‑velden storen beveiliging. Voer de passes uit die bij je reviewers passen, in welke volgorde dan ook, want elke pass schrijft zijn eigen output‑kopie. Wanneer niemand overlevende velden nodig heeft, ga direct naar sanitize() en verifieer.

Vergelijking van de drie benaderingen

Methode Beste voor Belangrijkste voordelen Beperkingen
Tag‑gedreven verwijdering Werkende kopieën, multi‑formaat pijplijnen Formaat‑onafhankelijk, behoudt beschrijvende velden Dekken alleen tag‑geclassificeerde concepten
Naam‑patroonverwijdering Opmerkingen, revisies, server‑velden Bereikt aangepaste eigenschappen die tags missen Substrings moeten per omgeving worden afgestemd
Volledige sanitize() Definitieve export buiten de organisatie Kan geen vergeten eigenschap missen Verwijdert ook onschuldige velden

De benaderingen combineren zich natuurlijk: gerichte passes terwijl het document leeft, sanitize() wanneer het wordt verzonden.

Verifieer voordat je erop vertrouwt

Een verwijderingsaanroep die een aantal teruggeeft, is geen bewijs dat het bestand schoon is. De repository sluit elke run af door de gesaniteerde output opnieuw te openen en te scannen met find_properties, gebruikmakend van een predicaat dat de tag‑regels en naam‑regels van alle bovenstaande passes combineert.

def is_pii(p):
    if p.name is None:
        return False
    return (
        Tags.person.creator in list(p.tags)
        or Tags.person.editor in list(p.tags)
        or Tags.person.manager in list(p.tags)
        or Tags.corporate.company in list(p.tags)
        or any(n in p.name for n in (
            "Comment", "Reviewer", "Revision", "TrackedChange",
            "Classification", "Department", "Server", "Workflow")))

with Metadata("board-report-final.docx") as metadata:
    for p in metadata.find_properties(is_pii):
        value = (str(p.interpreted_value) if p.interpreted_value is not None
                 else (str(p.value) if p.value is not None else ""))
        if value and value not in ("0", "0.0"):
            print(f"LEAK {p.name}={value}")

De volledige versie in de repository sorteert overgebleven items in twee bakken, en dat onderscheid is belangrijk. Metadata‑lekken moeten nul zijn. Restanten op inhoudsniveau, Word‑commentaarballonnen en tracked changes die zich binnen word/document.xml bevinden, zijn body‑content die een metadata‑API niet kan bereiken; het verwijderen daarvan vereist een content‑bewerkingsbibliotheek zoals Aspose.Words. Een eerlijk rapport noemt beide bakken in plaats van de overwinning alleen op de eerste te verklaren. De eerste keer dat ik deze scan op een “schone” file draaide, markeerde hij een Department‑veld dat een bedrijfs‑sjabloon stilletjes maandenlang had toegevoegd.

Best practices en tips

  • Sanitiseer kopieën, nooit originelen: elk fragment hier schrijft naar een nieuw pad, zodat de bron behouden blijft voor je administratie en retentie‑regels.
  • Log de aantallen: de retourwaarden van remove_properties en sanitize() vormen je auditspoor. Bewaar ze per bestand, per pass.
  • Integreer verificatie in CI: een lek‑check die de build faalt, vangt sjabloon‑regressies op de dag dat ze gebeuren, niet op de dag dat een klant het opmerkt.
  • Let op de grens tussen metadata en content: rapporteer een bestand nooit als schoon terwijl body‑level opmerkingen blijven; breng ze als een afzonderlijke bevinding naar voren.
  • Licenties: evaluatiemodus reproduceert alles in dit artikel; gebruik een licentie in productie zodat er geen evaluatiemerkjes op uitgaande bestanden staan.

Conclusie

Drie benaderingen, één beslissingsregel. Match op tag wanneer het concept geclassificeerd is en het bestand bruikbaar moet blijven. Match op naam wanneer de familie zich in aangepaste eigenschappen bevindt. Roep sanitize() aan wanneer het bestand de vertrouwensgrens overschrijdt, en verifieer met een terug‑scan, ongeacht welke route je hebt gekozen.

Klaar om dieper te gaan? Hier zijn enkele vervolgstappen:

Aanvullende bronnen

Heb je vragen of wil je je implementatie delen? Neem contact op via het support forum.