Jak automatycznie maskować dane paszportowe i numery kart z logów
Ostatnio badaliśmy incydent w środowisku produkcyjnym, gdzie awaria usługi spowodowała zrzut pełnego JSON-a z ciałem żądania użytkownika do ELK. Zawierał on numer karty kredytowej, adres zamieszkania i numer telefonu. Rejestrowanie wszystkiego w celach debugowania jest powszechną praktyką, ale gdy takie zrzuty trafiają do magazynu danych, zespoły bezpieczeństwa zadają sobie trudne pytania. Pisanie wzorców wyrażeń regularnych dla każdej drobnej rzeczy szybko staje się nużące. Łamią się przy złożonych formatach, pomijają nazwy i mylą losowe sekwencje liczb z prawdziwymi identyfikatorami.
Istnieją różne sposoby rozwiązania tego problemu. Jednym z najbardziej dojrzałych narzędzi open-source jest Presidio, rozwijane przez społeczność w organizacji data-privacy-stack.
Co kryje się pod maską i dlaczego tego potrzebujesz
Presidio pomaga znajdować i maskować dane osobowe (PII) w tekstach, arkuszach kalkulacyjnych, a nawet obrazach. Projekt został pierwotnie stworzony przez inżynierów Microsoftu jako biblioteka open source, a następnie przeniesiony do oddzielnej organizacji, zgromadziwszy ponad 10 000 gwiazdek na GitHubie.
Główna koncepcja jest prosta: podzielić zadanie ochrony prywatności na dwa niezależne kroki — analizę i anonimizację.
Zamiast jednego monolitycznego skryptu otrzymujesz dwa oddzielne moduły:
presidio-analyzer— sprawdza przychodzący tekst, znajduje encje takie jak numery paszportów, numery telefonów, adresy e-mail lub imiona i nazwiska, a następnie zwraca współrzędne wyników wraz z wynikiem pewności.presidio-anonymizer— pobiera znaczniki z analizatora i stosuje do nich reguły transformacji: zastępuje symbolami zastępczymi, szyfruje, maskuje gwiazdkami lub po prostu usuwa.
Ta separacja jest wygodna, gdy chcesz rejestrować tylko fakt wykrycia danych bez modyfikowania samego tekstu, lub gdy chcesz elastycznie konfigurować metodę maskowania dla różnych typów pól.
Jak działa wykrywanie encji
Jeśli próbowałeś parsować tekst samymi wyrażeniami regularnymi, znasz ich główną wadę: wyrażenia regularne nie rozumieją kontekstu. Znajdą sekwencję 16 cyfr, ale nie będą wiedziały, czy to numer karty, czy kod produktu w magazynie.
Presidio łączy wiele podejść jednocześnie:
- Wyrażenia regularne i sumy kontrolne (na przykład algorytm Luhna dla numerów kart bankowych lub walidatory IBAN).
- Wstępnie wytrenowane modele NLP oparte na spaCy lub Hugging Face Transformers do rozpoznawania nazwanych encji (NER) — ludzie, lokalizacje, organizacje.
- Kontekstowa analiza słów. Jeśli sekwencji liczb towarzyszą słowa takie jak "telefon", "tel" czy "zadzwoń", wynik pewności detektora wzrasta.
- Słowniki i listy słów blokowanych.
Przykład w Pythonie
Przyjrzyjmy się, jak wygląda podstawowy potok w Pythonie. Najpierw zainstaluj pakiety:
pip install presidio-analyzer presidio-anonymizer
python -m spacy download en_core_web_lg
Następnie uruchom analizator, aby znaleźć podatne miejsca w ciągu znaków:
from presidio_analyzer import AnalyzerEngine
from presidio_anonymizer import AnonymizerEngine
text = "Привет, меня зовут John Doe. Мой рабочий email john.doe@example.com, а телефон +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}")
Po tym, jak analizator zwróci przesunięcia i typy encji, połącz moduł anonimizacji:
anonymizer = AnonymizerEngine()
anonymized_result = anonymizer.anonymize(
text=text,
analyzer_results=results
)
print("\nРезультат обработки:")
print(anonymized_result.text)
Wynikiem jest ciąg znaków, w którym imiona i nazwiska są zastępowane przez <PERSON>, a dane kontaktowe przez <EMAIL_ADDRESS> i <PHONE_NUMBER>. W razie potrzeby można skonfigurować hashowanie, częściowe maskowanie znaków lub zastępowanie fałszywymi danymi za pomocą Fakera.
Dodawanie własnych reguł
Out of the box, projekt jest dobrze dostosowany do formatów międzynarodowych: amerykańskie numery ubezpieczenia społecznego, karty Visa i Mastercard, międzynarodowe numery telefonów, e-mail. W przypadku danych lokalnych, takich jak rosyjskie paszporty, INN czy SNILS, konieczne będzie napisanie własnych recognizerów.
Można to zrobić dość prosto, korzystając z wbudowanych klas:
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)
Gdy analizator wykryje dopasowanie do wyrażenia regularnego i znajdzie w pobliżu słowa z listy context, wynik pewności wzrasta do maksimum.
Pozostałe funkcje biblioteki
Oprócz pracy ze zwykłym tekstem, repozytorium zawiera moduły pomocnicze do powiązanych zadań:
presidio-image-redactor— znajduje tekst na obrazach i skanach PDF za pomocą OCR (Tesseract) i zamazuje wykryte dane osobowe czarnymi prostokątami bezpośrednio w pikselach. Przydatne do przetwarzania skanów paszportów przed wysłaniem do zewnętrznych usług.- Integracja z potokami LLM. Presidio jest często używane jako middleware przed wysłaniem promptów do OpenAI lub Anthropic: najpierw usuwasz dane osobowe użytkownika z żądania, wysyłasz zanonimizowany tekst do modelu, a w razie potrzeby przywracasz dane z powrotem.
- REST API na FastAPI, spakowane w kontenery Docker. Nie musisz pisać całej usługi w Pythonie — możesz wdrożyć analizator jako mikrousługę i wywoływać ją z backendów napisanych w Go, Javie lub Node.js.
Potencjalne wyzwania
Pierwszy niuans to wysokie zapotrzebowanie na zasoby. Jeśli połączysz ciężkie transformatory jak RoBERTa czy BERT dla dokładniejszego rozpoznawania encji, wnioskowanie będzie wymagać pamięci i spowolni przetwarzanie strumienia danych. Uruchomienie pełnego potoku NLP synchronicznie przy setkach tysięcy RPS będzie obciążające — trzeba będzie przenieść maskowanie do asynchronicznych workerów Kafka lub Celery.
Drugi punkt dotyczy modeli językowych dla języka rosyjskiego. Podstawowy model en_core_web_lg z spaCy dobrze radzi sobie z angielskim, ale dla rosyjskiego trzeba będzie albo połączyć model ru_core_news_lg, albo dostroić własny recognizer NER pod specyfikę własnego tekstu.
Praktyczne zastosowania
Presidio przydaje się, gdy firma musi spełnić wymogi Federalnego Prawa 152, RODO lub HIPAA, ale przepisywanie istniejącej infrastruktury od zera jest zbyt kosztowne.
Biblioteka dobrze sprawdza się w trzech scenariuszach:
- Czyszczenie logów i śladów przed wysłaniem do systemów monitoringu (Sentry, OpenTelemetry, ELK).
- Przygotowywanie zbiorów danych do trenowania modeli ML lub analiz na prawdziwych danych użytkowników.
- Filtrowanie przychodzących i wychodzących wiadomości w chatbotach i systemach RAG opartych na dużych modelach językowych.
Jeśli szukasz gotowego zestawu narzędzi do maskowania danych bez konieczności pisania parserów od zera, repozytorium zdecydowanie warto dodać do zakładek i przetestować lokalnie z własnymi przykładami.
Powiązane projekty