--- title: "Cómo anonimizar IPs y datos personales en logs de Nginx" description: "Anonimiza las IP en los logs de Nginx al escribirlos, elimina parámetros con datos personales y seudonimiza logs antiguos en Ubuntu 24.04 según el RGPD." category: "Cumplimiento de las normas" language: es source: https://cubepath.com/es/docs/cumplimiento-de-las-normas/anonimizacion-de-logs updated: 2026-09-25 --- # Cómo anonimizar IPs y datos personales en logs de Nginx Los logs de un servidor web contienen datos personales aunque no lo parezca: la dirección IP de cada visitante, y a menudo correos, nombres o tokens que llegan en los parámetros de la URL. Según el RGPD, una IP es un dato personal, así que conservar esos logs durante meses sin necesidad es un riesgo legal y de seguridad. En este tutorial configurarás Nginx en Ubuntu 24.04 para que escriba los logs ya anonimizados, dejarás un log completo de retención corta para seguridad y crearás un script para seudonimizar logs antiguos antes de archivarlos o compartirlos. ## Requisitos previos - Un servidor con Ubuntu 24.04 LTS con Nginx instalado (`sudo apt install nginx`), por ejemplo un VPS de CubePath. - Un usuario no root con privilegios `sudo`. - Conocer qué logs necesitas para seguridad (por ejemplo, si usas fail2ban sobre los logs de Nginx). ## Paso 1: Localizar los datos personales en tus logs Antes de cambiar nada, comprueba qué datos personales aparecen realmente. Estas búsquedas cuentan las líneas que contienen direcciones de correo y parámetros de URL con nombres sospechosos: ```bash sudo zgrep -cE '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}' /var/log/nginx/access.log* sudo zgrep -oE '[?&](email|user|name|phone|token|dni)=[^& ]*' /var/log/nginx/access.log* | sort | uniq -c | sort -rn | head ``` ```output /var/log/nginx/access.log:14 /var/log/nginx/access.log.1:22 /var/log/nginx/access.log.2.gz:9 8 /var/log/nginx/access.log:?email=ana.garcia@example.com 3 /var/log/nginx/access.log:?token=eyJhbGciOi... ``` Estos resultados te dicen qué tienes que tratar además de la IP. Que haya correos o tokens en las URL también es un problema de la aplicación: los datos sensibles deben enviarse en el cuerpo de una petición `POST`, no en la URL. Las técnicas que vas a aplicar son de dos tipos: | Técnica | Ejemplo | ¿Sigue siendo dato personal? | |---------|---------|------------------------------| | Truncado (anonimización) | `203.0.113.45` pasa a `203.0.113.0` | Reduce mucho la identificación; en la práctica suele considerarse suficiente para estadísticas | | Eliminación | Quitar la cadena de consulta de la URL | No, el dato desaparece | | Seudonimización con clave | `ana.garcia@example.com` pasa a `9f2c1a7b04de@example.com` | Sí: quien tenga la clave puede relacionarlo, pero no se lee a simple vista | ## Paso 2: Crear un formato de log anonimizado en Nginx Nginx permite calcular una variable nueva a partir de otra con la directiva `map`. Vas a crear `$remote_addr_anon`, que pone a cero el último octeto de las IPv4 y conserva solo los dos primeros grupos (un /32) de las IPv6. Crea un fichero en `/etc/nginx/conf.d/`, que Nginx incluye dentro del bloque `http`: ```bash sudo nano /etc/nginx/conf.d/logs-anonimizados.conf ``` ```nginx map $remote_addr $remote_addr_anon { ~^(?P\d+\.\d+\.\d+)\.\d+$ $ip.0; ~^(?P[0-9a-f]+:[0-9a-f]+): $ip::; default 0.0.0.0; } log_format anonimizado '$remote_addr_anon - - [$time_local] ' '"$request_method $uri $server_protocol" $status $body_bytes_sent ' '"$http_user_agent" $request_time'; ``` Este formato difiere del `combined` por defecto en tres puntos: - Usa `$remote_addr_anon` en lugar de la IP real. - Registra `$uri` (la ruta sin la cadena de consulta) en lugar de `$request`, así que los parámetros como `?email=...` no llegan al log. - No registra `$remote_user` ni `$http_referer`, que pueden contener nombres de usuario y URL de otras webs con datos personales. Ahora indica a Nginx que use este formato. En Ubuntu, el log de acceso se define en el fichero principal: ```bash sudo nano /etc/nginx/nginx.conf ``` Dentro del bloque `http`, busca la línea `access_log /var/log/nginx/access.log;` y cámbiala por: ```nginx access_log /var/log/nginx/access.log anonimizado; ``` Si alguno de tus bloques `server` en `/etc/nginx/sites-available/` define su propio `access_log`, añade también `anonimizado` al final de esa línea; si no, seguirá usando el formato completo. Comprueba la sintaxis y recarga Nginx: ```bash sudo nginx -t sudo systemctl reload nginx ``` ```output nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful ``` Haz una petición con un parámetro y mira la última línea del log: ```bash curl -4 -s -o /dev/null "http://localhost/?email=prueba@example.com" sudo tail -n 1 /var/log/nginx/access.log ``` ```output 127.0.0.0 - - [25/Sep/2026:10:44:12 +0000] "GET / HTTP/1.1" 200 615 "curl/8.5.0" 0.000 ``` La IP aparece truncada y el correo del parámetro ha desaparecido. Pide a alguien que acceda desde fuera o prueba desde tu equipo para ver una IP pública anonimizada. ## Paso 3: Mantener un log completo de retención corta Con IPs truncadas ya no puedes bloquear a un atacante concreto con herramientas como fail2ban, ni investigar un incidente reciente. El RGPD permite tratar datos personales por interés legítimo de seguridad si los conservas solo el tiempo necesario. Una solución habitual es escribir dos logs: uno completo que se borra pronto y otro anonimizado para estadísticas y archivo. Edita de nuevo `/etc/nginx/nginx.conf` y añade una segunda línea `access_log` justo después de la anterior: ```nginx access_log /var/log/nginx/access.log anonimizado; access_log /var/log/nginx/security.log; ``` `security.log` usa el formato `combined` por defecto, con IP completa. Recarga Nginx: ```bash sudo nginx -t && sudo systemctl reload nginx ``` La rotación de Nginx en Ubuntu, definida en `/etc/logrotate.d/nginx`, se aplica a todos los ficheros `/var/log/nginx/*.log` y conserva 14 días. Si quieres una retención distinta para cada fichero, sepáralos en bloques de logrotate propios como se explica en la guía de rotación y retención de logs. Si usas fail2ban, apunta sus jails de Nginx a `/var/log/nginx/security.log`. > Nota: El log de errores de Nginx (`/var/log/nginx/error.log`) incluye la IP del cliente en cada mensaje y su formato no se puede cambiar. Trátalo como el log de seguridad: retención corta y acceso restringido. ## Paso 4: Seudonimizar logs antiguos antes de archivarlos Los logs escritos antes del cambio, o los de otras aplicaciones que no puedes configurar, necesitan un tratamiento posterior. Este script lee un log (comprimido o no), trunca las IPv4 e IPv6 y sustituye la parte local de cada correo por un HMAC-SHA256 con una clave secreta. Usar una clave evita que alguien recupere los correos probando direcciones conocidas, y mantiene el mismo seudónimo para el mismo correo, lo que permite seguir contando usuarios únicos. Genera la clave y guárdala con permisos solo para root: ```bash sudo install -m 600 /dev/null /etc/anon-logs.key openssl rand -hex 32 | sudo tee /etc/anon-logs.key > /dev/null ``` Crea el script: ```bash sudo nano /usr/local/sbin/anonimizar-log ``` ```bash #!/usr/bin/env bash # Uso: anonimizar-log ORIGEN DESTINO.gz set -euo pipefail if [[ $# -ne 2 ]]; then echo "Uso: $0 ORIGEN DESTINO.gz" >&2 exit 1 fi src="$1" dst="$2" ANON_KEY="$(< /etc/anon-logs.key)" export ANON_KEY zcat -f -- "$src" | perl -MDigest::SHA=hmac_sha256_hex -pe ' s/\b(\d{1,3}\.\d{1,3}\.\d{1,3})\.\d{1,3}\b/$1.0/g; s/^([0-9a-f]{1,4}:[0-9a-f]{1,4}):[0-9a-f:]*/$1::/i; s/([A-Za-z0-9._%+-]+)@([A-Za-z0-9.-]+\.[A-Za-z]{2,})/substr(hmac_sha256_hex(lc $1, $ENV{ANON_KEY}), 0, 12) . "\@" . $2/ge; ' | gzip > "$dst" ``` Qué hace cada sustitución: 1. Pone a cero el último octeto de cualquier IPv4 de la línea. 2. Recorta la IPv6 del principio de la línea (donde Nginx escribe la IP del cliente) a sus dos primeros grupos. 3. Sustituye la parte local de cada correo por los 12 primeros caracteres de su HMAC y conserva el dominio. Hazlo ejecutable: ```bash sudo chmod 755 /usr/local/sbin/anonimizar-log ``` Pruébalo con un fichero de ejemplo: ```bash printf '%s\n' \ '203.0.113.45 - - [25/Sep/2026:10:12:01 +0000] "GET /login?email=ana.garcia@example.com HTTP/1.1" 200 512 "-" "curl/8.5.0"' \ '2001:db8:85a3::8a2e:370:7334 - - [25/Sep/2026:10:12:05 +0000] "POST /api/pedidos HTTP/1.1" 201 88 "-" "Mozilla/5.0"' \ > /tmp/prueba.log sudo anonimizar-log /tmp/prueba.log /tmp/prueba-anon.log.gz zcat /tmp/prueba-anon.log.gz ``` ```output 203.0.113.0 - - [25/Sep/2026:10:12:01 +0000] "GET /login?email=9f2c1a7b04de@example.com HTTP/1.1" 200 512 "-" "curl/8.5.0" 2001:db8:: - - [25/Sep/2026:10:12:05 +0000] "POST /api/pedidos HTTP/1.1" 201 88 "-" "Mozilla/5.0" ``` El seudónimo del correo será distinto en tu servidor porque depende de tu clave. Aplica el script a los logs rotados antes de moverlos al archivo, y borra después el original: ```bash sudo anonimizar-log /var/log/nginx/access.log.2.gz /var/archivo/nginx/access-20260923.anon.log.gz ``` Crea antes el directorio de destino (`sudo mkdir -p /var/archivo/nginx`) y comprueba el resultado con `zcat` antes de borrar nada. > Advertencia: La expresión de IPv4 también transforma cualquier otro texto con forma de IP, como algunos números de versión en el User-Agent. Es un efecto aceptable para logs de archivo, pero revisa el resultado si otras herramientas procesan esos ficheros. ## Paso 5: Comprobar que no se escapan datos personales Repite las búsquedas del paso 1 sobre los logs nuevos y los archivados. No deberían aparecer correos en claro ni IPs completas: ```bash sudo grep -cE '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}' /var/log/nginx/access.log sudo grep -oE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' /var/log/nginx/access.log | grep -vc '\.0$' ``` ```output 0 0 ``` El segundo comando cuenta las IPv4 al principio de la línea que no terminan en `.0`; debe devolver `0`. Programa esta comprobación de vez en cuando: un cambio en la aplicación o en un bloque `server` puede volver a introducir datos personales sin que nadie lo note. ## Solución de problemas - **`nginx: [emerg] unknown "remote_addr_anon" variable`**: Nginx no está cargando el fichero que contiene el `map`. Comprueba que `/etc/nginx/nginx.conf` incluye `/etc/nginx/conf.d/*.conf` dentro del bloque `http`. - **Todas las IPs aparecen como `0.0.0.0`**: la IP no encaja en ninguna expresión. Si Nginx está detrás de un proxy o un balanceador y usas el módulo `realip`, comprueba que `$remote_addr` contiene la IP del cliente y no la del proxy. - **Siguen apareciendo IPs completas en `access.log`**: algún bloque `server` define su propio `access_log` sin el formato `anonimizado`. Búscalo con `grep -rn access_log /etc/nginx/`. ## Conclusión Nginx escribe ahora un log de acceso sin IPs completas ni parámetros de URL, conserva un log completo con retención corta para seguridad y dispones de un script para seudonimizar logs antiguos antes de archivarlos. Como siguientes pasos, revisa los logs de tu aplicación con las mismas búsquedas del paso 1, documenta en tu registro de actividades de tratamiento qué logs guardas y durante cuánto tiempo, y ajusta la retención de cada fichero con logrotate.