Cómo funciona esta comprobación
Esta comprobación solo actúa si tu web es WordPress, y lo detecta buscando wp-content o wp-includes en el HTML de la página principal. Si no aparecen, no hace nada. Si aparecen, prueba tres fugas habituales. Primera: la etiqueta <meta name="generator" content="WordPress 6.x"> en el HTML, que revela la versión exacta (severidad baja). Segunda: el archivo /readme.html, que WordPress incluye en cada instalación y que también dice la versión (baja). Tercera: la ruta /wp-json/wp/v2/users de la REST API; si responde 200 con JSON que contiene los campos slug y name, está entregando la lista de usuarios con su nombre de login (severidad media).
Por qué importa
WordPress mueve más del 40 % de la web, así que es el objetivo favorito de los ataques automatizados. Esos ataques funcionan en dos pasos: identificar la versión y los plugins para elegir exploit, y después probar contraseñas contra el login. La versión expuesta resuelve el primer paso. La lista de usuarios resuelve la mitad del segundo: con el slug de cada usuario (que es su nombre de login, no el nombre visible), al atacante solo le queda la contraseña, y la de admin, editor o el nombre del dueño se prueba a miles por minuto. Por eso la enumeración de usuarios tiene severidad media y las otras dos, baja.
Cómo arreglarlo
Versión en el meta generator
En functions.php de tu tema hijo o en un plugin propio:
remove_action('wp_head', 'wp_generator');
Ten en cuenta que la versión también se ve en los parámetros ?ver=6.x de los CSS y JS del core. Ocultarlo del todo requiere un plugin de seguridad; pero lo que de verdad te protege es tener WordPress actualizado, no esconder el número.
readme.html
Bórralo tras cada instalación y actualización (WordPress lo vuelve a crear), o bloquéalo en el servidor. En nginx: location = /readme.html { return 404; }. En Apache, en el .htaccess: <Files readme.html> Require all denied </Files>. Haz lo mismo con license.txt.
Enumeración de usuarios
Restringe la ruta de usuarios de la REST API a sesiones autenticadas. Sin plugin, en functions.php:
add_filter('rest_endpoints', function ($endpoints) {
if (!is_user_logged_in()) {
unset($endpoints['/wp/v2/users']);
unset($endpoints['/wp/v2/users/(?P<id>[\d]+)']);
}
return $endpoints;
});
Los plugins de seguridad habituales (Wordfence, Solid Security, All-In-One Security) tienen una opción equivalente. Cierra también la enumeración antigua por ?author=1, que redirige a /author/nombre/, y pon límite de intentos y doble factor en el login: aunque el nombre de usuario se filtre, la contraseña deja de ser suficiente.
Comprueba que está arreglado
curl -s https://tudominio.com | grep -io '<meta name="generator"[^>]*>'
curl -s -o /dev/null -w "%{http_code}\n" https://tudominio.com/readme.html
curl -s -o /dev/null -w "%{http_code}\n" https://tudominio.com/wp-json/wp/v2/users
Esperado: la primera no devuelve nada; la segunda, 404 o 403; la tercera, 401 o 403 (o un 404 si eliminaste la ruta).
Preguntas frecuentes
¿No basta con cambiar el usuario "admin" por otro nombre?
Ayuda contra los ataques más tontos, pero si la REST API lista los usuarios, el nombre nuevo se descubre en una petición. Cambia el nombre y cierra la enumeración.
Mi tema o un plugin usa la REST API de usuarios en el front.
Es raro, pero pasa con algunos directorios de autores. Entonces, en lugar de quitar la ruta, limita los campos que devuelve (sin slug) o restríngela por capacidad de usuario. Pruébalo en un entorno de pruebas antes.
¿Esta comprobación mira plugins vulnerables?
No; mira fugas de la instalación base. Los plugins desactualizados se detectan en parte en librerías desactualizadas. Y si tienes activado WP_DEBUG_LOG, el archivo público que genera lo cubre logs expuestos.