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 |
| 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
12.847 pacientes
→
pg_dump
Solo esquema
sin datos
sin datos
→
Enmascarar
9 campos PII
SQL + Python
SQL + Python
→
Presidio Scan
Validar PII
residual = 0
residual = 0
→
Dev DB
medtrack.dev
Datos seguros
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 enmascaradosHECHO
-
Vistas BD + roles restringen acceso PII crudoHECHO
-
Pipeline prod → dev documentado y probadoHECHO
-
Presidio NLP scan validación residual PII = 0HECHO
-
Enmascaramiento tarjetas PCI-DSS BIN+4HECHO
-
Respuestas API enmascaran PII para roles no adminHECHO
-
Pipeline CI/CD: scan secretos hardcodedPENDIENTE — Sprint 14
-
Revisión anual del plan de enmascaramientoPROGRAMADO — 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.