11°
Portada del artículo: pg_anon encontró los emails de mi base, pero no los RUT
PostgreSQLDatos personalesAnonimizaciónpg_anonBases de datos

pg_anon encontró los emails de mi base, pero no los RUT

Probé pg_anon, la herramienta rusa que enmascara datos personales en Postgres. Con las reglas de fábrica detectó 1 de 8 columnas con PII en una base chilena; con reglas propias, las 8. Medí recall, integridad y velocidad contra pg_dump.

Efrain Garay 5 de septiembre de 2026

En 47 segundos: pg_anon detectó 1 de 8 columnas con datos personales en una base chilena, y las 8 cuando le escribí las reglas. El RUT es ciego de fábrica.Verlo en el visualizador de reels →

Quiero darle a un desarrollador una copia de la base de producción. El problema es obvio: esa base tiene nombres, correos, teléfonos y RUT (el número de identificación nacional chileno) de gente real. Copiarla tal cual a un ambiente de pruebas es filtrar datos personales, y la nueva ley chilena que castiga eso entra en vigencia en diciembre de 2026. Lo que necesito es un pg_dump que además tape lo sensible en el camino.

Eso promete pg_anon, una herramienta abierta de TantorLabs que apareció en Habr y que, hasta donde busqué, nadie ha probado en español. La instalé, le pasé una base chilena de juguete y medí tres cosas: cuánto de la información personal encuentra sola, si la copia enmascarada sigue siendo una base coherente, y cuánto cuesta contra un pg_dump normal.

Qué es, y en qué se distingue de pg_dump

pg_anon no reemplaza a pg_dump: lo envuelve. El flujo tiene cuatro pasos. Primero init crea en la base de origen un esquema anon_funcs con las funciones que después reemplazan valores. Luego create-dict escanea la base y arma un diccionario de qué columnas son sensibles. dump exporta los datos aplicando ese diccionario, y ahí ocurre el enmascarado. Por último restore carga la copia limpia en una base vacía.

La pieza que decide todo es el escaneo. Y el escaneo funciona con reglas que uno le da, en una cascada de dos filtros: primero lee el nombre de cada columna contra unas expresiones regulares; a las que sobran les abre los datos y prueba otras expresiones sobre el valor. Lo que ningún filtro atrapa, pasa de largo. Ahí está el detalle que las reseñas que leí pasan por alto.

El escaneo, filtro por filtroel orden real de create_dict.py
8 columnas con PII
nombreapellidoemailtelefonorutdireccionfecha_nactarjeta_ult4
1
por nombrelee la etiqueta de la columna contra field.rules
nombreapellidoemailtelefonorutdireccionfecha_nactarjeta_ult4
2
por contenidosolo a las que sobran: abre los datos y prueba data_regex
nombreapellidoemailtelefonorutdireccionfecha_nactarjeta_ult4
sensible
pasa de largo
reglas de fábrica inglés1 / 8solo email, por su nombre y su @
reglas en español + RUT8 / 8las 8 caen en el filtro 1, por su nombre
Medido sobre una tabla de 500 filas. El RUT solo lo atrapa una regla escrita a mano.

La instalación, con sus tropiezos

Levanté un PostgreSQL 17 en Docker y una base con dos tablas: clientes (500 filas, con nombre, apellido, email, teléfono, RUT, dirección, comuna, fecha de nacimiento y notas) y pedidos (2000 filas, con clave foránea a clientes y los últimos cuatro dígitos de la tarjeta). Ocho de esas columnas son datos personales. Datos falsos pero con la forma real de los datos chilenos: RUT calculados con su módulo 11, celulares +569, correos armados con nombre y apellido.

Instalar pg_anon adentro del contenedor pidió pip install --break-system-packages, porque Debian bloquea pip global desde bookworm, y nada más. Versión 1.11.0. Necesita pg_dump y pg_restore de la misma versión mayor que el servidor, cosa que el contenedor ya trae.

El hallazgo: detecta lo que sabe nombrar

Corrí el escaneo dos veces sobre la misma base. Primero con el meta-dict de fábrica (el archivo de reglas que guía el escaneo), con sus patrones en inglés. Después con un meta-dict propio: los mismos nombres de columna pero en español, más una expresión regular para el RUT.

Qué marcó como dato personal
reglas de fábrica: 1 / 8reglas en español + RUT: 8 / 8
columnareglas de fábricareglas en español + RUT
nombretext
apellidotext
emailtext
telefonotext
ruttext
direcciontext
fecha_nacdate
tarjeta_ult4char(4)
Recall sobre las 8 columnas con PII de la tabla clientes

Con las reglas de fábrica, pg_anon marcó una sola de las ocho columnas con datos personales: email. La pescó porque email se escribe igual en inglés y calza con una regla de nombre de fábrica; su contenido, con el arroba, la confirma por partida doble. Las otras siete (nombre, apellido, teléfono, dirección, fecha de nacimiento, los cuatro dígitos de la tarjeta y el RUT) pasaron de largo. El RUT es el caso más claro: ninguna regla de fábrica reconoce un identificador chileno, y su forma (siete u ocho dígitos, un guion y un dígito verificador) no se parece a nada que el diccionario por defecto busque.

Con el meta-dict adaptado, el recall saltó a ocho de ocho, sin un solo falso positivo: dejó intactas la comuna, las notas y las columnas que no son datos personales. La herramienta detecta bien; el trabajo es enseñarle el idioma y los identificadores del país.

La copia sí es una base coherente

Con el diccionario completo, el dump y el restore reconstruyeron la base en una copia vacía. Verifiqué lo que importa de un enmascarado: que lo sensible esté tapado y que lo demás siga sirviendo.

ComprobaciónResultado
Columnas con PII enmascaradas8 de 8
Filas preservadas500 clientes, 2000 pedidos (idéntico)
Pedidos huérfanos tras el restore0
Emails únicos tras el hash499 de 500

El cliente 1 pasó de Fernanda / fernanda.diaz4@outlook.com / 7917183-2 a tres hashes SHA-256. La integridad referencial quedó perfecta: cero pedidos apuntando a un cliente inexistente, porque las claves foráneas van por id, que no se toca. Y el hash conserva la unicidad: el origen tenía 499 correos distintos entre las 500 filas, y tras el hash siguen siendo 499, así que agrupar por correo cuenta lo mismo.

Lo que cuesta

Sobre 2500 filas, el enmascarado se paga en tiempo y en tamaño:

Operaciónpg_anonpg_dump normal
Exportar (dump)0,33 s0,064 s
Tamaño del volcado168 KB72 KB

Cinco veces más lento y más del doble de pesado. Es esperable: por cada valor sensible ejecuta una función SQL, y un hash de 64 caracteres pesa más que el correo o el RUT que reemplaza. En una base de 2500 filas no se nota; en una de millones habría que medirlo de nuevo antes de prometer nada.

Cuándo lo usaría, y cuándo no

, cuando necesito una copia de producción para desarrollo o pruebas y el esquema es estable: el trabajo de escribir el diccionario se hace una vez y queda. La integridad referencial que preserva es el punto fuerte, porque es justo lo que se rompe cuando uno intenta anonimizar a mano con UPDATE.

No, si espero que detecte los datos personales sola en una base en español. No lo hace: hay que escribirle las reglas, columna por columna, y verificar el recall con una base conocida antes de confiar en la copia. Y una advertencia que la propia documentación hace y conviene repetir: esto es seudonimización, no anonimización en el sentido de la GDPR. El hash lo deja claro: sin sal, un RUT tiene solo unos 25 millones de valores posibles, así que quien tenga el hash lo revierte enumerando en minutos, y lo mismo vale para un teléfono o una fecha. Reduce la exposición; no la elimina.

pg_anon es una buena herramienta a la que hay que hablarle en su idioma. Para una base chilena, ese idioma incluye una regla para el RUT que nadie va a escribir por ti. Así que la dejé escrita: el meta-dict en español, con la regla del RUT, está en un repositorio público para que no tengas que empezar de cero.

Fuentes

Comentarios

Todavía no hay comentarios. El primero es tuyo.

Se revisa antes de publicarse. El correo no se guarda ni aparece en ninguna parte.