secaudit.eu
Protección

Enumeración de usuarios en WordPress: REST API y autores

Enumeración de usuarios en WordPress: compruebe qué logins filtra su web por REST API, URL de autor, sitemap y errores de acceso, y aplique correcciones seguras.

Por , AngelForce OÜ 7 min de lectura

También en: English Deutsch Français Italiano Polski Português Türkçe

En una instalación estándar de WordPress, cualquiera suele poder averiguar sus nombres de acceso en menos de un minuto. La enumeración de usuarios en WordPress funciona a través de cinco o seis canales integrados: la REST API, las redirecciones de ?author=1, las URL de archivo de autor, el sitemap de usuarios, los mensajes de error del login y, en algunos casos, los feeds y los datos oEmbed. Este artículo explica cómo comprobar cada canal en su propio sitio y cómo cerrarlo con una corrección acotada que no rompa el editor de bloques.

Qué es la enumeración de usuarios en WordPress y cuánto importa

Enumerar usuarios consiste en recopilar nombres de cuenta válidos de un sitio. Un atacante que sabe que existe mkeller puede gastar cada intento de contraseña, y cada contraseña filtrada en otras brechas, contra una cuenta real en lugar de probar admin, info y test.

Conviene tener claro qué consigue ocultar los nombres: es un obstáculo menor, no una protección. Lo que impide que alguien se apodere de una cuenta es la autenticación en dos pasos y la limitación de intentos de acceso, así que configure primero esas dos medidas (autenticación en dos pasos para WordPress, protección contra fuerza bruta). Después cierre las fugas que siguen, en este orden: probar, corregir, volver a probar.

Diagrama: cinco canales que filtran nombres de acceso de WordPress, cada uno con su corrección
Por dónde se filtran los nombres de acceso y la corrección acotada para cada canal

Pruebe su propio sitio: seis lugares donde se filtran los usuarios

Ejecute estas pruebas solo contra sitios que sean suyos o que usted gestione. Hágalo sin sesión iniciada, desde un terminal o una ventana privada del navegador, porque una sesión abierta ve más datos. Sustituya example.com por su dominio.

  1. REST API. El núcleo lista los usuarios que han publicado contenido. Fíjese en el campo slug, que es el slug del autor y suele coincidir con el nombre de acceso.
curl -s https://example.com/wp-json/wp/v2/users | grep -o '"slug":"[^"]*"'
curl -s "https://example.com/?rest_route=/wp/v2/users" | grep -o '"slug":"[^"]*"'
  1. Redirecciones por ID de autor. WordPress redirige ?author=N a la URL amigable del autor. Pruebe con 1, 2, 3 y así sucesivamente.
curl -sI "https://example.com/?author=1" | grep -i '^location'
  1. HTML del tema. Enlaces de autor en las entradas y clases CSS como comment-author-<slug> en los comentarios de usuarios registrados.
curl -s https://example.com/a-post/ | grep -oE '/author/[^/"]+/|comment-author-[a-z0-9-]+' | sort -u
  1. Sitemap de usuarios. Abra https://example.com/wp-sitemap-users-1.xml en el navegador.
  1. Errores de login. En wp-login.php, introduzca un usuario inventado y después uno real con una contraseña incorrecta. Si los mensajes son distintos, el formulario está confirmando cuentas válidas. El formulario de contraseña olvidada se comporta igual. Limítese a dos o tres intentos para que su propia limitación no le bloquee.
  1. Feeds y oEmbed. El feed muestra los nombres públicos en dc:creator. El endpoint oEmbed devuelve author_name y author_url, y esa URL contiene el slug del autor.
curl -s https://example.com/feed/ | grep -i 'dc:creator'
curl -s "https://example.com/wp-json/oembed/1.0/embed?url=https://example.com/a-post/"
CanalPruebaRevela
Usuarios REST/wp-json/wp/v2/usersSlug, nombre, ID
Redirección de autor/?author=1Slug en Location
HTML del temaMarcado de entradas y comentariosSlug
Sitemap de usuarios/wp-sitemap-users-1.xmlURL de autor con slug
Errores de loginUsuario inexistente vs. contraseña erróneaLogins válidos
Feed / oEmbeddc:creator, author_urlNombre público, slug

Si prefiere un método estructurado, la OWASP Web Security Testing Guide trata las pruebas de enumeración de cuentas con más detalle.

Restrinja el endpoint REST de usuarios solo para visitantes anónimos

Actúe únicamente sobre la ruta de usuarios y solo para peticiones sin usuario autenticado. El editor de bloques usa /wp/v2/users para el selector de autor, y las apps móviles y muchos plugins lo llaman con autenticación. Si elimina la ruta para todos, dejarán de funcionar.

Cree wp-content/mu-plugins/user-enumeration.php. Los plugins de uso obligatorio (must-use) se cargan automáticamente y no se pueden desactivar desde el escritorio. El filtro rest_endpoints permite descartar rutas antes de que se sirvan:

<?php
/**
 * Plugin Name: Limit user enumeration
 */

add_filter( 'rest_endpoints', function ( $endpoints ) {
    if ( is_user_logged_in() ) {
        return $endpoints;
    }
    unset( $endpoints['/wp/v2/users'] );
    unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] );
    return $endpoints;
} );

Repita la prueba con curl. Ahora debería recibir un error rest_no_route con estado 404. Después inicie sesión, abra una entrada en el editor y compruebe que el panel de autor sigue listando usuarios.

Si utiliza un front-end headless o una app que lee datos de autor de forma anónima, este fragmento la romperá. En ese caso, exponga solo los campos que necesite mediante un endpoint propio. La referencia de usuarios de la REST API enumera lo que devuelve el endpoint.

Detenga las redirecciones por ID de autor y decida si necesita archivos de autor

Cuando alguien solicita /?author=2, WordPress resuelve el usuario y su redirección canónica envía al visitante a /author/<slug>/. La cabecera Location delata el slug, y un script puede recorrer los ID del 1 al 50 en segundos.

Añada esto debajo del primer fragmento, en el mismo mu-plugin. Se ejecuta antes de la redirección canónica y responde con un 404 simple a las peticiones anónimas con parámetro author, de modo que ninguna cabecera Location revela el slug:

add_action( 'template_redirect', function () {
    if ( is_user_logged_in() ) {
        return;
    }
    if ( isset( $_GET['author'] ) ) {
        wp_die( 'Not found.', '', array( 'response' => 404 ) );
    }
}, 1 );

Vuelva a probar con curl -sI "https://example.com/?author=1". Ahora debería obtener un 404 y ninguna cabecera Location.

En el blog de una empresa con un solo autor, o en un sitio sin páginas públicas de autor, vaya más lejos y desactive los archivos de autor. La mayoría de los plugins SEO tienen un ajuste para ello, y algunos temas también. Y una limitación: si su tema imprime enlaces «por <autor>» en cada entrada, el slug sigue estando en el HTML. De eso se ocupan las dos secciones siguientes.

Elimine el sitemap de usuarios donde nadie lo necesita

Los sitemaps del núcleo, incorporados en WordPress 5.5, incluyen un proveedor de usuarios que lista todos los archivos de autor. Pocos sitios de empresa sacan algún provecho de ello. El filtro wp_sitemaps_add_provider lo elimina:

add_filter( 'wp_sitemaps_add_provider', function ( $provider, $name ) {
    if ( 'users' === $name ) {
        return false;
    }
    return $provider;
}, 10, 2 );

Abra de nuevo /wp-sitemap-users-1.xml; ahora debería devolver un 404. Si un plugin SEO sustituye los sitemaps del núcleo, este filtro no afecta a lo que genera ese plugin. Busque en sus ajustes de sitemap un sitemap de autores y desactívelo allí.

Haga que los nombres públicos y los slugs de autor difieran de los nombres de acceso

En Usuarios > Perfil, defina un Apodo y elíjalo en Mostrar este nombre públicamente. Eso cubre los feeds, el author_name de oEmbed y las líneas «por».

Aquí está la trampa: el slug de autor (user_nicename) se genera a partir del nombre de acceso al crear la cuenta y no cambia al modificar el nombre público. Sus entradas pueden decir «Redacción» mientras la URL sigue siendo /author/mkeller/. Se corrige con WP-CLI:

wp db export before-nicename.sql
wp user list --fields=ID,user_login,user_nicename,display_name
wp user update 2 --user_nicename=editorial-team

Use letras minúsculas, dígitos y guiones, y mantenga cada slug único. Las antiguas URL de autor cambian, así que cabe esperar algunos 404; si esas páginas recibían tráfico, añada una redirección.

En las cuentas nuevas, evite admin, el nombre de la empresa y el nombre del dominio como nombre de acceso. Si parte del problema son cuentas antiguas o compartidas, límpielas primero: seguridad de los roles de usuario en WordPress.

Mensajes de error de login genéricos

El filtro login_errors sustituye los mensajes de la pantalla de acceso por un único texto neutro:

add_filter( 'login_errors', function () {
    return '<strong>Error:</strong> The login details are incorrect.';
} );

Ponga el texto del mensaje en el idioma de su sitio. El filtro también sustituye mensajes útiles, como el aviso de campo vacío, de modo que sus usuarios reciben menos orientación. Tampoco vuelve opaco el sitio: el formulario de contraseña olvidada sigue comportándose de forma distinta con cuentas válidas, ya que una cuenta real conduce a la pantalla «revise su correo». Algunos plugins añaden sus propios formularios de acceso, y la página Mi cuenta de WooCommerce tiene mensajes propios. Considere esta medida un tema de coherencia, no un control sólido.

Lo que ocultar nombres de usuario no detiene

Los atacantes prueban nombres y direcciones de correo habituales tanto si su sitio los filtra como si no, y WordPress acepta la dirección de correo como nombre de acceso. El relleno de credenciales (credential stuffing), que reutiliza pares de usuario y contraseña de otras brechas, no necesita ninguna enumeración. Una lista de usuarios perfectamente oculta no sirve de nada frente a una contraseña reutilizada.

Por eso, el orden para el sitio de una pyme es este:

  1. Autenticación en dos pasos para todos los administradores y editores (guía de configuración).
  2. Limitación de intentos de acceso y una decisión sobre XML-RPC (protección contra fuerza bruta).
  3. Las correcciones de enumeración anteriores, comprobadas con los comandos curl.
  4. Incorporar el resultado a su revisión periódica (lista de comprobación de seguridad de WordPress).

El plugin Security Audit & Compliance de secaudit.eu incluye limitación de intentos de acceso y limitación de la tasa de peticiones REST anónimas, y su análisis de hardening informa de hallazgos de configuración para que vea qué sigue abierto → secaudit.eu

Su siguiente paso: ejecute hoy mismo en su sitio las seis pruebas de este artículo, anote los resultados y corrija los canales que muestren un nombre de acceso real.

Preguntas frecuentes

¿Bloquear /wp/v2/users rompe el editor de bloques?

No, si lo restringe a las peticiones anónimas, como en el fragmento anterior. El editor envía peticiones autenticadas, así que el selector de autor sigue funcionando. Eliminar la ruta también para los usuarios con sesión iniciada sí lo rompería.

¿Sigue siendo «admin» un riesgo si tengo 2FA?

Mucho menos. Con 2FA, una contraseña adivinada no basta para entrar. Pero admin atrae intentos constantes, que llenan los registros y pueden provocar bloqueos, así que sigue mereciendo la pena sustituirlo.

¿Debo cambiar el nombre de acceso y cómo?

WordPress no tiene ningún botón para cambiar el nombre de acceso. Cree un nuevo administrador con un nombre neutro y 2FA, inicie sesión con él, elimine después la cuenta antigua y asigne su contenido a la nueva cuando WordPress se lo pregunte. Mantenga siempre un administrador operativo. Los detalles están en seguridad de los roles de usuario en WordPress.

Compruebe su propio sitio

Security Audit & Compliance es el plugin de WordPress de secaudit.eu: busca malware, spam oculto, archivos del núcleo modificados y código vulnerable, y relaciona sus hallazgos con el art. 32 del RGPD, el art. 21 de NIS2 y el OWASP Top 10.

Ver qué comprueba el plugin
Melike Görel

Melike Görel escribe las guías de secaudit.eu sobre seguridad de WordPress y cumplimiento normativo europeo para AngelForce OÜ, la empresa de software de Tallin que está detrás de secaudit.eu.

Elaborado con ayuda de IA y verificado antes de su publicación. Este artículo es información general, no asesoramiento jurídico.