Fuerza Bruta en WordPress: Cómo Bloquear Ataques en el Login

PUNTOS CLAVE

  • WordPress permite intentos ilimitados de acceso por defecto, facilitando ataques de fuerza bruta.
  • Los ataques suelen automatizarse contra los archivos wp-login.php y xmlrpc.php.
  • Implementar un bloqueo temporal basado en IP reduce drásticamente el uso de CPU.
  • La gestión mediante transients permite una implementación ligera sin saturar la base de datos física.

Dejar la puerta del panel de administración abierta a infinitos intentos es un riesgo innecesario. Los bots modernos no descansan. Un ataque de fuerza bruta no solo busca adivinar tu contraseña, sino que puede tumbar tu servidor por agotamiento de recursos si no limitas las peticiones. Limitar los intentos de login en WordPress es una medida de higiene básica que debería venir configurada de serie, pero que como desarrolladores nos vemos obligados a implementar nosotros mismos para evitar desastres.

#¿Cómo funciona un ataque de fuerza bruta?

Un bot utiliza diccionarios de contraseñas. Miles de combinaciones por minuto. Es llamativo que el estándar siga confiando en que el usuario elegirá una clave compleja en lugar de implementar un bloqueo reactivo tras fallar tres veces. Aunque uses plugins de seguridad pesados, entender cómo filtrar estas peticiones en la capa de autenticación te da un control mucho más granular sobre el rendimiento de tu sitio.

TÉRMINO RELACIONADO

Ataque de Diccionario: Una técnica de hacking que consiste en probar sistemáticamente palabras de una lista predefinida para encontrar una contraseña válida.

#Limitando intentos mediante Transients API

Vamos a usar un filtro de WordPress para rastrear los fallos de acceso asociados a una dirección IP. Usar la Transients API es ideal en este escenario, ya que nos permite almacenar datos de forma temporal y dejar que el sistema de caché profesional (como Redis o Memcached) se encargue de la limpieza automática.

#Añadir al archivo functions.php de tu tema:

php
/**
 * Rastreo de intentos fallidos y bloqueo por IP.
 * Bloquea al usuario tras 5 intentos fallidos durante 15 minutos.
 */

add_filter( 'wp_authenticate_user', function( $user, $password ) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $transient_name = 'limit_login_' . str_replace( '.', '_', $ip );
    $attempts = get_transient( $transient_name );

    if ( $attempts && $attempts >= 5 ) {
        // El usuario está bloqueado. Lanzamos el error antes de procesar nada más.
        return new WP_Error( 'too_many_attempts', 'DEMASIADOS INTENTOS: Tu IP ha sido bloqueada temporalmente por seguridad.' );
    }

    return $user;
}, 10, 2 );

add_action( 'wp_login_failed', function( $username ) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $transient_name = 'limit_login_' . str_replace( '.', '_', $ip );
    $attempts = get_transient( $transient_name );

    $attempts = $attempts ? $attempts + 1 : 1;
    // Guardamos el incremento con una expiración de 15 minutos (900 segundos)
    set_transient( $transient_name, $attempts, 900 );
} );
TIP PRO

No olvides desactivar también el archivo xmlrpc.php si no lo usas para la App de WordPress o Jetpack. Es el punto de entrada favorito para ataques de fuerza bruta masiva ya que permite probar cientos de contraseñas en una sola petición.

#Caso real: El servidor que «moría» cada lunes a las 9 AM

Hicimos un seguimiento para un cliente que experimentaba caídas constantes al inicio de cada semana. El servidor simplemente dejaba de responder. Lo curioso es que el tráfico de Analytics era normal. No había más usuarios de lo habitual. El problema era invisible para las herramientas de marketing.

Al revisar los logs del servidor localmente, vi la causa. No era un error de código. Era una IP atacando el formulario de login con una cadencia de 20 peticiones por segundo. El síntoma clínico apareció en el log de errores como un agotamiento de procesos PHP-FPM. El cliente intentó cambiar la contraseña, pero el bot seguía intentando entrar con la antigua, consumiendo los mismos recursos.

Fue el tipo de error que ves en producción a las 2 AM y juras que no volverá a pasar. Implementé el bloqueo por transientes que ves arriba y la carga de CPU bajó del 90% al 5% en cuestión de segundos. El bot, al recibir un error 403 instantáneo sin que WordPress tuviera que validar la contraseña contra la base de datos, dejó de ser una amenaza para el rendimiento general.

#Protocolo de rescate: ¿Te has bloqueado a ti mismo?

Si por error has fallado demasiadas veces y tu propia IP ha quedado bloqueada, no podrás entrar al panel. Mantén la calma. Es el resultado esperado de una buena medida de seguridad.

Para recuperar el acceso, tienes dos opciones rápidas. La más sencilla es cambiar tu IP (reiniciando el router o usando una VPN). Si no puedes, accede a tu base de datos mediante phpMyAdmin o WP-CLI y busca en la tabla wp_options las entradas que empiecen por _transient_limit_login_. Borra la fila que corresponda a tu IP y el acceso se restaurará de inmediato. No es un fallo del sistema, es su correcto funcionamiento.

#¿Dónde aplica esta técnica?

  • Escenarios recomendados: Blogs personales, webs corporativas y cualquier sitio WordPress que no use un firewall de red externo (WAF) como Cloudflare.
  • Contraindicaciones: No lo uses si ya tienes un sistema de seguridad avanzado a nivel de servidor o si tu flujo de trabajo implica scripts que necesitan loguearse automáticamente y podrían disparar el bloqueo.

#Pros y Contras

  • Pros: Ahorro drástico de recursos de servidor, protección contra accesos no autorizados y configuración ligera sin dependencias externas.
  • Contras: Puede bloquear a usuarios legítimos por errores tipográficos y los transientes pueden «ensuciar» la tabla de opciones si no hay un sistema de caché de objetos activo.

«La autenticación fuerte comienza por no dejar que el atacante intente la llave más de tres veces.»

— Ingeniero de Ciberseguridad en Plataformas CMS

Esto lo escribo en febrero de 2026. Resulta curioso —por no decir frustrante— que la solución oficial siga pasando por parches de este tipo o plugins de terceros, pero hasta que el core no integre una política de seguridad de login nativa, este snippet sigue siendo un salvavidas necesario.

Parámetro Valor Sugerido Objetivo
Intentos Máximos 5 fallos Prevenir Fuerza Bruta
Tiempo de Bloqueo 900 segundos Enfriamiento de Ataque
Almacenamiento Transients Rendimiento DB

Para mejorar aún más la seguridad, consulta la documentación sobre ataques de fuerza bruta en WordPress o revisa cómo configurar Cloudflare WAF para detener estas amenazas en la nube.