HIPAA & GDPR-Compliant Local PII Redaction: Sanitizing Customer Data with Microsoft Presidio Before Cloud LLM Inference

HIPAA & GDPR-Compliant Local PII Redaction: Sanitizing Customer Data with Microsoft Presidio Before Cloud LLM Inference

(Updated: ) 📖 1 min read

Under HIPAA, GDPR, and SOC2 Type II, sending raw patient records, employee Social Security numbers, or customer credit card details to third-party cloud APIs can result in severe compliance fines.

The solution is an in-memory PII Sanitization Gateway: identifying and replacing sensitive entities with synthetic tokens (<PERSON_1>, <SSN_1>) on your local server before making the LLM request.


1. Reversible Masking Architecture

User Prompt (with PII)
       │
       ▼
┌─────────────────────────────┐
│ Local Presidio Analyzer     │ ➔ Identifies "John Doe", "4412-XXXX"
└──────────────┬──────────────┘
               │
               ▼
┌─────────────────────────────┐
│ Local Presidio Anonymizer   │ ➔ Replaces with <PERSON_1>, <CARD_1>
└──────────────┬──────────────┘
               │ (Stores reverse mapping in RAM)
               ▼
┌─────────────────────────────┐
│ Cloud LLM (Gemini / OpenAI) │ ➔ Receives zero private customer PII!
└──────────────┬──────────────┘
               │
               ▼
┌─────────────────────────────┐
│ Local Deanonymizer          │ ➔ Replaces <PERSON_1> back with "John Doe"
└──────────────┬──────────────┘
               │
               ▼
Clean Response Returned to User

2. Complete Python Implementation

from presidio_analyzer import AnalyzerEngine
from presidio_anonymizer import AnonymizerEngine
from presidio_anonymizer.entities import OperatorConfig

analyzer = AnalyzerEngine()
anonymizer = AnonymizerEngine()

def mask_customer_prompt(raw_text: str):
    # 1. Detect PII entities
    results = analyzer.analyze(
        text=raw_text,
        language="en",
        entities=["PERSON", "EMAIL_ADDRESS", "PHONE_NUMBER", "US_SSN", "CREDIT_CARD"]
    )

    # 2. Anonymize with synthetic replace tokens
    anonymized = anonymizer.anonymize(
        text=raw_text,
        analyzer_results=results,
        operators={"DEFAULT": OperatorConfig("replace", {"new_value": "<REDACTED>"})}
    )

    return anonymized.text

# Test input
sample = "Patient John Doe (SSN: 000-12-3456) reported dizziness after taking medication."
clean = mask_customer_prompt(sample)
print(clean)
# Output: Patient <REDACTED> (SSN: <REDACTED>) reported dizziness after taking medication.
WEEKLY NEWSLETTER

Get Weekly AI Architect Cost & Strategy Updates

Join 14,000+ developers receiving weekly, data-driven cost-reduction blueprints and production-ready agent guidelines.

Professor XAI
Professor XAI ML Engineer passionate about advancing AI technologies and building intelligent systems.
comments powered by Disqus