Comment masquer automatiquement les données de passeport et les numéros de carte dans les logs
Récemment, nous enquêtions sur un incident en production où un service planté avait déversé un JSON complet avec le corps de la requête de l'utilisateur dans ELK. Il contenait un numéro de carte bancaire, une adresse domicile et un numéro de téléphone. Enregistrer tout à des fins de débogage est une pratique courante, mais lorsque de tels dumps se retrouvent dans le stockage, les équipes de sécurité posent des questions très inconfortables. Écrire des regex pour chaque petite chose devient vite fastidieux. Elles cassent sur les formats complexes, passent à côté des noms et confondent des séquences de nombres aléatoires avec de véritables identifiants.
Il existe différentes approches pour résoudre ce problème. L'un des outils open source les plus matures disponibles est Presidio, développé par la communauté sous l'organisation data-privacy-stack.
Ce qui se cache sous le capot et pourquoi vous en avez besoin
Presidio aide à trouver et masquer les informations personnellement identifiables (PII) dans les textes, les tableurs et même les images. Le projet a été initialement créé par des ingénieurs Microsoft comme bibliothèque open source, et a depuis été transféré vers une organisation distincte, totalisant plus de 10 000 étoiles sur GitHub.
L'idée centrale est simple : diviser la tâche de protection de la vie privée en deux étapes indépendantes — l'analyse et l'anonymisation.
Au lieu d'un script monolithique, vous obtenez deux modules séparés :
presidio-analyzer— examine le texte entrant, trouve des entités comme des numéros de passeport, des numéros de téléphone, des adresses email ou des noms, et renvoie les coordonnées des découvertes ainsi qu'un score de confiance.presidio-anonymizer— prend le balisage de l'analyseur et applique des règles de transformation : remplace par des placeholders, chiffre, masque avec des astérisques, ou supprime simplement.
Cette séparation est pratique lorsque vous devez enregistrer uniquement le fait de la détection de données sans modifier le texte lui-même, ou lorsque vous souhaitez configurer de manière flexible la méthode de masquage pour différents types de champs.
Comment fonctionne la détection d'entités
Si vous avez déjà essayé d'analyser du texte avec uniquement des expressions régulières, vous connaissez leur principal défaut : les regex ne comprennent pas le contexte. Elles trouveront une séquence de 16 chiffres, mais ne sauront pas s'il s'agit d'un numéro de carte ou d'une référence produit dans un entrepôt.
Presidio combine plusieurs approches à la fois :
- Des expressions régulières et des sommes de contrôle (par exemple, l'algorithme de Luhn pour les numéros de carte bancaire ou les validateurs IBAN).
- Des modèles NLP pré-entraînés basés sur spaCy ou Hugging Face Transformers pour la reconnaissance d'entités nommées (NER) — personnes, lieux, organisations.
- L'analyse contextuelle des mots. Si une séquence de nombres est précédée de mots comme « téléphone », « tel » ou « appel », le score de confiance du détecteur augmente.
- Des dictionnaires et des listes de mots vides.
Exemple Python
Voyons à quoi ressemble un pipeline Python de base. Commencez par installer les paquets :
pip install presidio-analyzer presidio-anonymizer
python -m spacy download en_core_web_lg
D'abord, lancez l'analyseur pour trouver les points vulnérables dans la chaîne :
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}")
Une fois que l'analyseur renvoie les décalages et les types d'entités, connectez le module d'anonymisation :
anonymizer = AnonymizerEngine()
anonymized_result = anonymizer.anonymize(
text=text,
analyzer_results=results
)
print("\nРезультат обработки:")
print(anonymized_result.text)
Le résultat est une chaîne où les noms sont remplacés par <PERSON> et les contacts par <EMAIL_ADDRESS> et <PHONE_NUMBER>. Si vous le souhaitez, vous pouvez configurer le hachage, le masquage partiel de caractères ou le remplacement par de fausses données via Faker.
Ajouter des règles personnalisées
Out of the box, le projet est bien réglé pour les formats internationaux : numéros de sécurité sociale américains, cartes Visa et Mastercard, numéros de téléphone internationaux, email. Pour les données locales comme les passeports russes, l'INN ou le SNILS, vous devrez écrire vos propres reconnaisseurs.
C'est assez simple à faire en utilisant les classes intégrées :
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)
Lorsque l'analyseur voit une correspondance avec l'expression régulière et trouve des mots de la liste context à proximité, il élève le score de confiance au maximum.
Autres fonctionnalités de la bibliothèque
En plus de travailler avec du texte brut, le dépôt contient des modules d'aide pour des tâches connexes :
presidio-image-redactor— trouve du texte dans les images et les scans PDF via OCR (Tesseract) et peint par-dessus les données personnelles détectées avec des rectangles noirs directement dans les pixels. Utile pour traiter les scans de passeport avant l'envoi à des services externes.- Intégration au pipeline LLM. Presidio est souvent utilisé comme middleware avant d'envoyer des prompts à OpenAI ou Anthropic : vous nettoyez les données personnelles de l'utilisateur de la requête, envoyez le texte anonymisé au modèle, et dans l'autre sens, restaurez les données à leur place si nécessaire.
- API REST sur FastAPI, packagée dans des conteneurs Docker. Vous n'avez pas besoin d'écrire l'ensemble du service en Python — vous pouvez déployer l'analyseur comme microservice et l'appeler depuis des backends Go, Java ou Node.js.
Défis potentiels
La première nuance est la gourmandise en ressources. Si vous connectez des transformers lourds comme RoBERTa ou BERT pour une reconnaissance d'entités plus précise, l'inférence nécessitera de la mémoire et ralentira le traitement du flux de données. Exécuter un pipeline NLP complet de manière synchrone sur des centaines de milliers de requêtes par seconde sera lourd — vous devrez décharger le masquage vers des workers Kafka ou Celery asynchrones.
Le deuxième point concerne les modèles de langue russe. Le modèle spaCy en_core_web_lg de base gère bien l'anglais, mais pour le russe, vous devrez soit connecter le modèle ru_core_news_lg, soit affiner votre propre reconnaisseur NER pour les spécificités de votre texte.
Applications pratiques
Presidio est utile lorsqu'une entreprise doit se conformer à la loi fédérale 152, au RGPD ou à la HIPAA, mais réécrire l'infrastructure existante de zéro est trop coûteux.
La bibliothèque s'intègre bien dans trois scénarios :
- Nettoyer les logs et les traces avant l'envoi vers les systèmes de surveillance (Sentry, OpenTelemetry, ELK).
- Préparer des ensembles de données pour l'entraînement de modèles ML ou l'analyse sur des données utilisateur réelles.
- Filtrer les messages entrants et sortants dans les chatbots et les systèmes RAG basés sur de grands modèles de langage.
Si vous cherchez une boîte à outils prête à l'emploi pour le masquage de données sans avoir à écrire des parseurs from scratch, le dépôt mérite définitivement d'être ajouté aux favoris et testé localement avec vos propres exemples.
Projets similaires