Cómo crear pipelines ELT para anonimizar PII: paso a paso
Aprende a anonimizar PII en ELT de forma práctica y segura: este tutorial describe por qué es importante tratar PII (Personally Identifiable Information), qué técnicas usar y cómo integrar esas transformaciones en un pipeline ELT para mantener los datos útiles para análisis sin exponer información sensible. Explicaré el motivo de cada elección y daré pasos concretos con ejemplos SQL que funcionan en motores compatibles (Databricks, Synapse, Snowflake).
Pre-requisitos
- Cuenta o entorno con un motor SQL/Delta compatible (p. ej.: Databricks, Synapse, Snowflake). Idealmente con 4–8 nodos para probar rendimiento en conjuntos de 1–10M registros.
- Fuente de datos con PII (CSV/JSON) accesible al entorno; un archivo de prueba con ~1000 filas ayuda a validar antes del rollout.
- Permisos para crear tablas, funciones y ejecutar jobs ELT; acceso a un servicio de tokenización si existe la necesidad de reversión.
- Conocimientos básicos de SQL y funciones de hashing/cryptography. Conocimientos de MERGE/UPSERT y particionamiento son útiles para pipelines idempotentes.
Paso 1: Identificar campos PII y definir requisitos de anonimización
Antes de codificar, haz un inventario de los campos PII (p. ej.: name, email, NIF, móvil). Para cada campo decide el método adecuado: enmascaramiento simple (mantiene parte legible), hashing irreversible (SHA256) cuando no necesitas revertir, tokenización cuando se necesita reversión controlada, o generalización cuando solo es necesaria agregación (p. ej.: almacenar solo el prefijo del móvil).
Ejemplo de mapeo para 5 campos:
- name: enmascaramiento (primeros 2 caracteres) — preserva segmentación por iniciales en informes;
- email: mantener dominio, hashear el user — útil para mantener métricas por dominio (p. ej.: gmail.com) sin exponer el user;
- NIF: hashing irreversible — obligatorio para impedir reconstrucción;
- móvil: generalización al código nacional (3 dígitos) — suficiente para análisis por región;
- created_at: mantener intacto para análisis temporales.
Paso 2: Crear tabla staging non-anonymized
Carga los datos originales a una tabla staging sin modificaciones. Esto permite auditoría, reconciliación y reprocesado. Mantén el staging en una zona segura (p. ej.: OneLake/secure storage) con control de acceso restringido al equipo responsable.
-- Exemplo SQL para Databricks/Synapse/Snowflake
CREATE OR REPLACE TABLE raw_customers_staging (
id STRING,
name STRING,
email STRING,
nif STRING,
phone STRING,
created_at TIMESTAMP
);
-- COPY/LOAD a partir de CSV ou caminho de origem (varia por plataforma)
-- Para grandes volumes recomenda-se particionar por created_at ou por dia.
Paso 3: Implementar funciones de anonimización reutilizables
Crea UDFs/SQL functions para que la lógica esté centralizada, testeable y auditable. Esto facilita actualizaciones (p. ej.: cambiar algoritmo de hashing) sin alterar múltiples queries. Testea las funciones con conjuntos de 1000–10.000 registros antes de usar en producción.
-- Hash irreversível (SHA256) e truncar para uma coluna identificadora
CREATE OR REPLACE FUNCTION hash_sha256(val STRING) RETURNS STRING AS (
sha2(val, 256)
);
-- Mascaramento: manter primeiros 2 caracteres e substituir o resto por X
CREATE OR REPLACE FUNCTION mask_name(val STRING) RETURNS STRING AS (
CASE WHEN val IS NULL THEN NULL
WHEN length(val) <= 2 THEN repeat('X', length(val))
ELSE substr(val,1,2) || repeat('X', length(val)-2)
END
);
-- Generalizar telemóvel: manter só prefixo nacional (ex.: primeiros 3 dígitos)
CREATE OR REPLACE FUNCTION generalize_phone(val STRING) RETURNS STRING AS (
CASE WHEN val IS NULL THEN NULL
WHEN length(regexp_replace(val,'\\D','')) <= 3 THEN 'REDACTED'
ELSE substr(regexp_replace(val,'\\D',''),1,3) || 'XXXXX'
END
);
Paso 4: Anonimizar con una query ELT y guardar en tabla target
Aplica las funciones en la transformación y escribe el resultado en la tabla final que se usará para informes. Mantén columnas no sensibles intactas y documenta claramente qué campos fueron transformados. Si necesitas revertir, integra un servicio de tokenización (gestionado y auditado) en lugar de hashing.
CREATE OR REPLACE TABLE customers_anonymized AS
SELECT
id,
mask_name(name) AS name_masked,
-- manter domínio: split email e hash o user
concat(hash_sha256(split_part(email,'@',1)), '@', split_part(email,'@',2)) AS email_anonymized,
-- NIF hashed para irreversibilidade
hash_sha256(nif) AS nif_hash,
generalize_phone(phone) AS phone_generalized,
created_at
FROM raw_customers_staging;
Paso 5: Automatizar ELT y validar idempotencia
Programa el job para ejecutarse periódicamente (p. ej.: cada hora o nightly). Asegura idempotencia usando MERGE/UPSERT y watermarking (por ejemplo, procesar solo records con created_at >= ultima_run). Testea con reruns: al recrear el pipeline 3 veces seguidas el número de registros en la tabla target debe permanecer estable.
-- Exemplo MERGE para atualização incremental
MERGE INTO customers_anonymized tgt
USING (SELECT * FROM raw_customers_staging WHERE created_at >= date_sub(current_date(),1)) src
ON tgt.id = src.id
WHEN MATCHED THEN UPDATE SET
name_masked = mask_name(src.name),
email_anonymized = concat(hash_sha256(split_part(src.email,'@',1)), '@', split_part(src.email,'@',2)),
nif_hash = hash_sha256(src.nif),
phone_generalized = generalize_phone(src.phone),
created_at = src.created_at
WHEN NOT MATCHED THEN INSERT VALUES (
src.id,
mask_name(src.name),
concat(hash_sha256(split_part(src.email,'@',1)), '@', split_part(src.email,'@',2)),
hash_sha256(src.nif),
generalize_phone(src.phone),
src.created_at
);
Verificar el resultado
Realiza comprobaciones automáticas y manuales: asegúrate de que no existen campos originales en la tabla anonymizada, que los formatos son consistentes y que el hashing tiene la longitud esperada (SHA256 → 64 hex chars). Ejemplos de queries de validación y métricas:
-- Amostra de 5 linhas para inspeção visual
SELECT id, name_masked, email_anonymized, phone_generalized FROM customers_anonymized LIMIT 5;
-- Verifica que nif_hash tem comprimento 64 (SHA256 hex)
SELECT DISTINCT length(nif_hash) AS len FROM customers_anonymized LIMIT 5;
-- Conta linhas para garantir idempotência após rerun
SELECT count(*) FROM customers_anonymized;
-- Percentagem de NULLs por coluna para validar qualidade
SELECT
sum(CASE WHEN name_masked IS NULL THEN 1 ELSE 0 END)/count(*) AS pct_null_name,
sum(CASE WHEN email_anonymized IS NULL THEN 1 ELSE 0 END)/count(*) AS pct_null_email
FROM customers_anonymized;
Conclusión
Anonimizar PII en ELT reduce riesgo y permite análisis útiles. Empieza pequeño (p. ej.: 10k registros) para validar corrección y rendimiento antes de escalar a millones de filas. Próximos pasos prácticos: implementar tokenización reversible si es necesario, añadir logging y auditoría (quién procesó el job, cuándo), y probar el rendimiento con datos en batch y streaming. Y, por supuesto, confirma requisitos legales y políticas internas de gobernanza antes del roll-out.