Cómo proteger WordPress de ataques de fuerza bruta con un sencillo plugin

Por Antonio Casado
Developer en OpenSistemas

La seguridad en Internet es cada día más complicada y aunque utilicemos herramientas seguras y verificadas como es el caso de WordPress, nuestra web siempre está disponible (24×7) para que un asaltante trate de hacerse con la contraseña a la fuerza.

Lo ideal sería elegir contraseñas robustas y difíciles de recordar y cambiar la contraseña habitualmente, pero no todos nuestros sitios web merecen tal cantidad de sacrificio y por eso existen algunos plugins de WordPress que nos ayudan a proteger nuestra web.

Sin embargo, aquí tenéis uno nuevo con el objetivo de ilustrar un poco el desarrollo de plugins y ofrecer ideas que podáis adaptar a vuestros desarrollos.

La idea del plugin es evitar que una máquina realice ataques de fuerza bruta en nuestro servidor. Normalmente, enviando paquetes con el par <usuario, contraseña> a nuestra página de login /wp-admin.php o /wp-login.php.

Primer paso: introducir un campo oculto nonce en el formulario mediante ajax.

php code:

add_action( 'wp_ajax_nopriv_load_wp_login_nonce', 'ajax_load_wp_login_nonce' );
function ajax_load_wp_login_nonce(){
    wp_nonce_field( 'login_timer', 'login_nonce' );
    die();
}
add_action( 'login_enqueue_scripts',   'login_enqueue_script' );
function login_enqueue_script() {
    $ajaxurl = admin_url( 'admin-ajax.php', 'relative' );
    wp_register_script('wp-login-timer-js', plugins_url("wp-login-timer.js", __FILE__), array('jquery'), '1.0.0');
    wp_enqueue_script('wp-login-timer-js');
    wp_localize_script('wp-login-timer-js', 'ajaxurl', $ajaxurl);
}

javascript code (wp-login-timer.js):

jQuery(document).ready(function () {
    jQuery.ajax({
        type: 'POST',
        url: ajaxurl,
        data: {
            'action': 'load_wp_login_nonce',
        },
        success: function (data) {
            jQuery('#loginform').append(data);
        },
        error: function (data) {
            console.log(data);
        }
    });
});

La función wp_nonce_field nos proporciona un valor aleatorio a modo de clave de seguridad que luego podremos verificar.

Utilizando ajax para introducir el campo nonce en el formulario, en lugar de introducirlo directamente, supone un paso adicional en la seguridad, ya que muchos robots son capaces de leer los campos ocultos del formulario y enviarlos.

No profundizaré sobre cómo realizar peticiones ajax en nuestro código para no extenderme, pero quien esté interesado en esta información puede consultar los hooks wp_ajax_(action) y wp_ajax_nopriv_(action) de la API de WP (https://codex.wordpress.org/Plugin_API/Action_Reference)

Segundo paso: verificar el campo nonce al realizar el login

php code:

add_action( 'wp_authenticate_user', 'check_custom_authentication', 10, 1 );
function check_custom_authentication($user) {
    if ( ! isset( $_POST['login_nonce'] )
        || ! wp_verify_nonce( $_POST['login_nonce'], 'login_timer' )
        ){
        return new WP_Error();
    }
    return $user;
}

Con este sencillo código podemos verificar si el formulario ha sido enviado con el campo nonce que previamente habíamos introducido.

Mejoras a tener en cuenta

Con este código ya ofrecemos cierta seguridad adicional a nuestro WordPress eliminando muchos de los ataques que nos podrían llegar mediante robots. No obstante, seguimos siendo vulnerables ante atacantes astutos, por ejemplo, robots que utilicen herramientas que puedan ejecutar código javascript como CasperJs.

Una posible mejora para este plugin sería introducir unos segundos de espera antes de enviar el campo nonce y limitar el número de intentos simultáneos a una IP.

Otra mejora, también muy interesante, para proteger nuestra web consiste en cambiar la URL de acceso al backend de la misma, es decir, /wp-admin/ por /otra-url/.

Si queréis podéis obtener un plugin totalmente funcional con el código que hemos visto en el enlace: https://github.com/acasado86/wp-login-timer.git