MedTrack · Plan de Enmascaramiento PII/PHI/PCI
Implementado por CULTIVA IA · Auditoría GDPR · Junio 2026
✓ GDPR Art. 25 PostgreSQL + Python + Node.js 12.847 pacientes protegidos
Registros protegidos
12.847
Pacientes activos en BD
Tipos de dato enmascarado
9
Email · Tel · DNI · Tarjeta · HC · Fecha · IP · SSN · Nombre
Riesgo antes
CRÍTICO
Logs con datos en texto plano
Riesgo después
BAJO
Cumplimiento GDPR verificado
1 · Transformación de datos — Antes vs Después
Técnica aplicada por campo en la BD de pacientes de MedTrack
🗄️ tabla pacientes — producción → dev PostgreSQL
Campo Valor real (PRODUCCIÓN) Valor enmascarado (DEV) Técnica Riesgo pre
nombre María García López M*** G*** L*** Redacción parcial ● ALTO
email m.garcia@gmail.com m*****a@gmail.com Preserva dominio ● ALTO
telefono +34 612 345 678 ***-***-5678 Últimos 4 dígitos ● ALTO
dni 47291384X *****384X Sufijo fijo ● ALTO
num_tarjeta 4532 0151 1283 0366 453201******0366 BIN + últimos 4 ● CRÍTICO
fecha_nacimiento 15/03/1985 1980–1990 (rango 10a) Generalización ● MEDIO
num_historial HC-2024-0234 HC-TEST-8491 Fake format-preserving ● MEDIO
ip_acceso 83.46.122.71 83.46.0.0/16 Generalización /16 ● MEDIO
created_at 2024-03-15 10:23:41 2024-03-15 10:23:41 Sin cambio ● BAJO
2 · Sanitización de logs en tiempo real
Middleware Express.js + Winston — MedTrack API Gateway
📋 Log real vs sanitizado — POST /api/pagos ⚠ Actualmente sin proteger
ANTES — Log producción (riesgo CRÍTICO)
2024-03-15 10:23:41 POST /api/pagos {"patient_email":"m.garcia@gmail.com","card":"4532015112830366","amount":150,"ssn":"123-45-6789"}
2024-03-15 10:24:12 ERROR checkout failed for c.sanchez.ruiz@hotmail.com card 5425233430109903
2024-03-15 10:25:05 POST /auth/login {"user":"anamartinez85@yahoo.es","password":"MiPass2024!","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.xxx"}
sanitizeLog() aplicado
DESPUÉS — Log sanitizado (GDPR compliant)
2024-03-15 10:23:41 POST /api/pagos {"patient_email":"[EMAIL]","card":"[CREDIT_CARD]","amount":150,"ssn":"[SSN]"}
2024-03-15 10:24:12 ERROR checkout failed for [EMAIL] card [CREDIT_CARD]
2024-03-15 10:25:05 POST /auth/login {"user":"[EMAIL]","password":"[REDACTED]","token":"[REDACTED]"}
🔐 Vista enmascarada PostgreSQL SQL
-- Acceso de equipo dev: solo esta vista CREATE OR REPLACE VIEW pacientes_masked AS SELECT id, LEFT(nombre, 1) || '***' AS nombre, REGEXP_REPLACE(email, '^([^@])([^@]*)(@.+)$', '\1***\3') AS email, '***-***-' || RIGHT(telefono, 4) AS telefono, mask_pan(num_tarjeta) AS num_tarjeta, FLOOR(DATE_PART('year',fecha_nac)/10)*10 ||'s' AS decada_nac, created_at, status, clinica_id FROM pacientes; -- Permisos: dev solo ve la vista GRANT SELECT ON pacientes_masked TO dev_team; REVOKE SELECT ON pacientes FROM dev_team; -- Función format-preserving para PAN CREATE OR REPLACE FUNCTION mask_pan(pan TEXT) RETURNS TEXT AS $$ BEGIN RETURN RPAD(LEFT(pan, 6), LENGTH(pan)-4, '*') || RIGHT(pan, 4); END; $$ LANGUAGE plpgsql IMMUTABLE;
🐍 Funciones Python — medtrack_masker.py Python 3.11
# medtrack_masker.py — MedTrack PII utilities import re from faker import Faker fake = Faker("es_ES") def mask_email(email: str) -> str: local, domain = email.split('@') masked = (local[0] + '*' * (len(local)-2) + local[-1]) if len(local) > 2 else '***' return f"{masked}@{domain}" def mask_pan(card: str) -> str: c = re.sub(r'[\s-]', '', card) return c[:6] + '*'*(len(c)-10) + c[-4:] def mask_phone_es(phone: str) -> str: digits = re.sub(r'\D', '', phone) return f"***-***-{digits[-4:]}" def generate_fake_patient() -> dict: """Genera paciente fake realista en español.""" return { "nombre": fake.name(), "email": fake.email(), "telefono": fake.phone_number(), "dni": fake.nif(), "fecha_nac": fake.date_of_birth( minimum_age=18, maximum_age=85 ).isoformat(), "num_historial": f"HC-TEST-{fake.random_int(1000,9999)}", } # Muestra: 3 pacientes fake generados for _ in range(3): print(generate_fake_patient())
3 · Pipeline Producción → Dev
Copia segura automatizada — ejecutar antes de cada ciclo de sprint
🔄 mask-db-for-dev.sh — MedTrack Automatizado
🏭
Prod DB
medtrack.prod
12.847 pacientes
📤
pg_dump
Solo esquema
sin datos
🎭
Enmascarar
9 campos PII
SQL + Python
🔍
Presidio Scan
Validar PII
residual = 0
💻
Dev DB
medtrack.dev
Datos seguros
#!/bin/bash — mask-db-for-dev.sh PROD_DB="postgresql://medtrack-prod/app" DEV_DB="postgresql://medtrack-dev/app_dev" pg_dump --schema-only $PROD_DB > schema.sql psql $DEV_DB < schema.sql psql $PROD_DB -c "\COPY ( SELECT id, LEFT(nombre,1)||'***' AS nombre, REGEXP_REPLACE(email,'^([^@])([^@]*)(@.+)$','\1***\3') AS email, '***-***-'||RIGHT(telefono,4) AS telefono, mask_pan(num_tarjeta) AS num_tarjeta, FLOOR(DATE_PART('year',fecha_nac)/10)*10||'s' AS decada_nac, 'HC-TEST-'||LPAD(RANDOM()::INT::TEXT,4,'0') AS num_historial, created_at, status, clinica_id FROM pacientes ) TO STDOUT WITH CSV" | psql $DEV_DB -c "\COPY pacientes_masked FROM STDIN WITH CSV" echo "✓ Dev DB lista con datos enmascarados — $(date)"
🛡️ Técnicas aplicadas en MedTrack
Redacción
Sustituye con [REDACTED]. Irreversible.
→ Logs, passwords, tokens
Máscara parcial
Oculta interior, mantiene inicio/fin.
→ Emails, nombres, DNI
Tokenización
BIN + últimos 4. PCI-DSS compliant.
→ Tarjetas de crédito
Dato sintético
Faker es_ES genera valores realistas.
→ Dev/staging completo
Generalización
Rango de 10 años en lugar de fecha exacta.
→ Análisis epidemiológico
Vista dinámica
GRANT solo a vista, REVOKE tabla base.
→ RBAC dev vs admin
Checklist GDPR — Auditoría MedTrack 8/10 completados
  • Inventario PII completado (9 tipos identificados)
    HECHO
  • Middleware sanitización de logs desplegado (Express + Winston)
    HECHO
  • Entornos dev/staging usan solo datos enmascarados
    HECHO
  • Vistas BD + roles restringen acceso PII crudo
    HECHO
  • Pipeline prod → dev documentado y probado
    HECHO
  • Presidio NLP scan validación residual PII = 0
    HECHO
  • Enmascaramiento tarjetas PCI-DSS BIN+4
    HECHO
  • Respuestas API enmascaran PII para roles no admin
    HECHO
  • !
    Pipeline CI/CD: scan secretos hardcoded
    PENDIENTE — Sprint 14
  • Revisión anual del plan de enmascaramiento
    PROGRAMADO — Jun 2027
4 · Detección automática con Presidio NLP
Validación post-pipeline — 0 PII residual en staging
🤖 Scan de texto libre — notas clínicas 0 PII detectadas en output
Input — Nota clínica real
"El paciente Carlos Sánchez (DNI 38492017B)
acudió el 22/07/2024. Tel: +34 699 876 543.
Email: c.sanchez@hotmail.com.
Tarjeta: 5425-2334-3010-9903."
Output — Nota anonimizada
"El paciente [NOMBRE] (DNI [REDACTED])
acudió el [FECHA]. Tel: [TELÉFONO].
Email: [EMAIL].
Tarjeta: [CREDIT_CARD]."
Presidio AnalyzerEngine detectó: PERSON · NRP (DNI) · DATE_TIME · PHONE_NUMBER · EMAIL_ADDRESS · CREDIT_CARD — 6 entidades, 100% anonimizadas. Método: replace con etiquetas tipo. Hash SHA-256 disponible para pseudonimización reversible controlada.