Paspoortgegevens en kaartnummers automatisch verwijderen uit logs
Onlangs onderzochten we een incident in productie waarbij een gecrashte service een volledige JSON dumpte met het request body van de gebruiker in ELK. Het bevatte een creditcardnummer, thuisadres en telefoonnummer. Alles loggen voor debugdoeleinden is gangbare praktijk, maar wanneer dergelijke dumps in opslag terechtkomen, komen beveiligingsteams met nogal ongemakkelijke vragen. Regex-patronen schrijven voor elk kleinigheidje wordt snel vervelend. Ze breken op complexe formaten, missen namen en verwarren willekeurige nummerreeksen met echte identificatoren.
Er zijn verschillende manieren om dit probleem op te lossen. Een van de meest volwassen open-source tools die beschikbaar zijn is Presidio, ontwikkeld door de community onder de data-privacy-stack organisatie.
Wat er onder de motorkap zit en waarom je het nodig hebt
Presidio helpt bij het vinden en maskeren van persoonlijk identificeerbare informatie (PII) in teksten, spreadsheets en zelfs afbeeldingen. Het project werd oorspronkelijk gemaakt door Microsoft-ingenieurs als een open bibliotheek, en is sindsdien verhuisd naar een aparte organisatie, met meer dan 10.000 sterren op GitHub.
De kerngedachte hier is simpel: splits de privacybeschermingstaak op in twee onafhankelijke stappen — analyse en anonimisering.
In plaats van één monolithisch script krijg je twee aparte modules:
presidio-analyzer— onderzoekt inkomende tekst, vindt entiteiten zoals paspoortnummers, telefoonnummers, e-mailadressen of namen, en retourneert de coördinaten van bevindingen samen met een betrouwbaarheidsscore.presidio-anonymizer— neemt de markup van de analyzer en past transformatieregels toe: vervangt met placeholders, versleutelt, maskeert met asterisken, of verwijdert simpelweg.
Deze scheiding is handig wanneer je alleen het feit van datadetectie wilt loggen zonder de tekst zelf te wijzigen, of wanneer je de maskeringsmethode flexibel wilt configureren voor verschillende veldtypen.
Hoe entiteitsdetectie werkt
Als je hebt geprobeerd tekst te parsen met reguliere expressies alleen, ken je hun belangrijkste nadeel: regex begrijpt geen context. Het vindt een reeks van 16 cijfers, maar weet niet of het een kaartnummer is of een product-SKU in een magazijn.
Presidio combineert meerdere benaderingen tegelijk:
- Reguliere expressies en checksums (bijvoorbeeld het Luhn-algoritme voor bankkaartnummers of IBAN-validators).
- Voortgetrainde NLP-modellen gebaseerd op spaCy of Hugging Face Transformers voor named entity recognition (NER) — personen, locaties, organisaties.
- Contextuele woordanalyse. Als een nummerreeks wordt voorafgegaan door woorden zoals "phone," "tel," of "call," verhoogt de betrouwbaarheidsscore van de detector.
- Woordenboeken en stopwoordenlijsten.
Python-voorbeeld
Laten we kijken hoe een basis Python-pipeline eruitziet. Installeer eerst de pakketten:
pip install presidio-analyzer presidio-anonymizer
python -m spacy download en_core_web_lg
Voer eerst de analyzer uit om kwetsbare plekken in de string te vinden:
from presidio_analyzer import AnalyzerEngine
from presidio_anonymizer import AnonymizerEngine
text = "Привет, меня зовут John Doe. Мой рабочий email [email protected], а телефон +1-555-0199."
analyzer = AnalyzerEngine()
results = analyzer.analyze(text=text, entities=["EMAIL_ADDRESS", "PHONE_NUMBER", "PERSON"], language='en')
print("Найденные сущности:")
for res in results:
print(f"Тип: {res.entity_type}, позиция: {res.start}:{res.end}, уверенность: {res.score:.2f}")
Nadat de analyzer offsets en entiteitstypen retourneert, koppel je het anonimisatiemodule:
anonymizer = AnonymizerEngine()
anonymized_result = anonymizer.anonymize(
text=text,
analyzer_results=results
)
print("\nРезультат обработки:")
print(anonymized_result.text)
De output is een string waarbij namen zijn vervangen door <PERSON> en contactgegevens zijn vervangen door <EMAIL_ADDRESS> en <PHONE_NUMBER>. Indien gewenst kun je hashing, gedeeltelijke tekenmaskering of vervanging door nepgegevens via Faker configureren.
Aangepaste regels toevoegen
Out of the box is het project goed afgestemd op internationale formaten: Amerikaanse burgerservicenummers, Visa- en Mastercard-kaarten, internationale telefoonnummers, e-mail. Voor lokale gegevens zoals Russische paspoorten, INN of SNILS, moet je je eigen recognizers schrijven.
Het is vrij eenvoudig te doen met ingebouwde klassen:
from presidio_analyzer import Pattern, PatternRecognizer
# Паттерн для поиска СНИЛС (формат XXX-XXX-XXX YY)
snils_pattern = Pattern(
name="snils_pattern",
regex=r"\b\d{3}-\d{3}-\d{3}\s\d{2}\b",
score=0.85
)
snils_recognizer = PatternRecognizer(
supported_entity="RU_SNILS",
patterns=[snils_pattern],
context=["снилс", "страховой", "пенсионный"]
)
analyzer.registry.add_recognizer(snils_recognizer)
Wanneer de analyzer een match tegen de reguliere expressie ziet en woorden uit de context lijst in de buurt vindt, verhoogt het de betrouwbaarheidsscore naar het maximum.
Andere bibliotheekfuncties
Naast het werken met platte tekst bevat de repository helpermodules voor gerelateerde taken:
presidio-image-redactor— vindt tekst in afbeeldingen en PDF-scans via OCR (Tesseract) en verft over gedetecteerde persoonlijke gegevens met zwarte vakken rechtstreeks in de pixels. Handig voor het verwerken van paspoortscans voordat ze naar externe services worden verzonden.- LLM pipeline-integratie. Presidio wordt vaak gebruikt als middleware voordat prompts naar OpenAI of Anthropic worden verzonden: je verwijdert de persoonlijke gegevens van de gebruiker uit het verzoek, stuurt de geanonimiseerde tekst naar het model, en op de terugweg herstel je de gegevens indien nodig op hun plaats.
- REST API op FastAPI, verpakt in Docker-containers. Je hoeft niet de hele service in Python te schrijven — je kunt de analyzer als microservice deployen en aanroepen vanuit Go-, Java- of Node.js-backends.
Mogelijke uitdagingen
Het eerste nuancepunt is honger naar resources. Als je zware transformers zoals RoBERTa of BERT aansluit voor nauwkeurigere entiteitsherkenning, vereist inferentie geheugen en vertraagt het de verwerking van datastromen. Een volledige NLP-pipeline synchroon draaien op honderdduizenden RPS zal zwaar zijn — je zult het maskeren moeten offloaden naar asynchrone Kafka- of Celery-workers.
Het tweede punt betreft Russische taalmodellen. De basis en_core_web_lg van spaCy werkt goed voor Engels, maar voor Russisch moet je ofwel het ru_core_news_lg model aansluiten of je eigen NER-recognizer finetunen voor je tekstspecificaties.
Praktische toepassingen
Presidio komt goed van pas wanneer een bedrijf moet voldoen aan federale wet 152, GDPR of HIPAA, maar het herschrijven van de bestaande infrastructuur van scratch te duur is.
De bibliotheek past goed in drie scenario's:
- Logs en traces scrubben voordat ze naar monitoringssystemen worden verzonden (Sentry, OpenTelemetry, ELK).
- Datasets voorbereiden voor ML-modeltraining of analytics op echte gebruikersgegevens.
- Inkomende en uitgaande berichten filteren in chatbots en RAG-systemen gebaseerd op grote taalmodellen.
Als je op zoek bent naar een kant-en-klare toolkit voor datamaskering zonder parsers vanaf nul te hoeven schrijven, is de repository zeker het bookmarken waard en lokaal testen met je eigen voorbeelden.
Gerelateerde projecten