Comment bloquer wp-login.php pour empêcher les attaques par force brute sur WordPress

Log d'une attaque par force brute sur un site WordPress
Log d’une attaque par force brute sur un site WordPress

Je commence avec un sujet récemment rencontré. WordPress a de nombreuses qualités mais ce CMS présent sur 25% des sites dans le monde a aussi de tristes défauts. Par sa célébrité, il est la cible privilégiée des attaques par force brute. Pour rappel, une attaque par force brute consiste à tester une à une des combinaisons d’identifiant de connexion + mot de passe. Au hasard, une combinaison telle que Admin et azerty.

Sur jeuxpcmag.com, c’est assez simple, j’en ai une dizaine par jour. Le site est sur un hébergement mutualisé, il y a bien un système de sécurisation mis en place par l’hébergeur mais le problème est que lorsque les attaques sont trop intenses, il coupe la totalité du site pendant une dizaine de minutes. Franchement pas top pour les visiteurs, comme je n’ai pas accès au paramétrage du serveur, j’ai été contraint de désactiver cette sécurité. J’ai installé un plugin permettant le blocage d’une IP pendant x minutes après y tentatives de connexion mais je ne suis pas satisfait car je ne fais que ralentir un robot actif 24h/24 et 7j/7. Autant dire que cela ne sert presque à rien.

Pour ce type d’attaque, il y a une solution évidente pour mettre à mal les robots. C’est de déplacer ou bloquer wp-login.php. Le constat est le suivant, une grande majorité des sites sous WordPress n’a cure de l’enregistrement d’utilisateurs et donc de leur connexion. Il faut juste permettre aux administrateurs, éditeurs et rédacteurs de se connecter. Il existe des plugins tel que SF Move Login (avec un français à la réalisation) qui permet de déplacer la page wp-login.php. Mais comme tout plugin, il doit s’adapter à plusieurs contextes (ex : Apache Vs. nginx), ce qui n’est pas forcément le plus optimisé (et puis vous n’apprenez rien).

J’aimerai aussi mettre en avant une demande souvent émise par les équipes marketing :

Il faut une page login personnalisée !

C’est le cas pour mon site de jeux vidéo, j’ai besoin que les utilisateurs puissent s’enregistrer (seul moyen pour commenter). C’est donc intéressant si je peux avoir des pages eye-candy (connexion, mot de passe oublié, enregistrement…). J’ai donc cherché une solution répondant aux deux problématiques.

Bloquer wp-login.php

En réalité, je ne le bloque pas vraiment, en matière de sécurité web, il est souvent pertinent d’induire le robot en erreur. Vous pouvez toujours accéder à la page de connexion de Webigon, je « tue » simplement la page avec un minuscule bout de code à placer dans le fichier functions.php de votre thème WordPress.

<?php
//Je crée une fonction pour réaliser ma petite affaire que je nomme disable_wplogin
function disable_wplogin() {
     die; //Je tue purement et simplement le processus enclenché par l'arrivée sur wp-login.php
}
//Pour que cela soit efficace, j'active ma fonction à la fin du chargement de l'en-tête de wp-login.php
add_action('login_head', 'disable_wplogin');
?>

En deux lignes de code, vous montrez page blanche à tout le monde. Ce qui est intéressant avec une telle fonction, c’est qu’il est possible de la développer pour afficher n’importe quoi. Si vous êtes un sacré farceur, vous pouvez remettre le formulaire de connexion et passer outre les valeurs que l’utilisateur (ou le robot) tentera de faire passer. De mon côté, je reste sympathique et affiche une erreur avec le même look que la page classique.

<?php
//Je crée une fonction pour réaliser ma petite affaire que je nomme disable_wplogin
function disable_wplogin() { ?>
    <div class="login">
    <div id="login">
		<a href="http://www.webigon.fr" title="Webigon.fr" style="text-align:center;width:100%;display:block;">[VOTRE_LOGO]</a>
	<div id="login_error">Webigon se fiche pas mal des comptes enregistrés. Il est inutile de tenter de vous connecter.</div>
	<p id="backtoblog"><a href="http://www.webigon.fr/" title="Êtes-vous perdu(e) ?">← Retour sur webigon.fr</a></p>
	</div>
    </div>
<?php
     die; //Je tue purement et simplement le processus enclenché par l'arrivée sur wp-login.php
}
//Pour que cela soit efficace, j'active ma fonction à la fin du chargement de l'en-tête de wp-login.php
add_action('login_head', 'disable_wplogin');
?>

Logiquement, je place tout mon HTML avant la fonction die et à l’intérieur de ma fonction disable_wplogin. La classe login, du moins dans la version 4.4 de WordPress, est indispensable pour la gestion des CSS tout comme les id parsemant mon code. J’en profite pour caser mon logo en SVG, je vous épargne le code que j’ai remplacé par [VOTRE_LOGO], vous pouvez insérer une image, du texte, peu importe.

Dans le lien de mon logo, vous voyez que j’ajoute quelques styles, ils servent à centrer mon logo SVG. A vous de voir au besoin, je l’ai laissé pour que vous compreniez bien que vous êtes libre d’ajouter ce que vous voulez. Enfin, j’ai mis mon petit message d’erreur et j’ai conservé le lien de retour pour faire gagner du temps à l’utilisateur aventurier.

Créer une page de connexion

C’est super, maintenant, plus personne ne peut se connecter, même pas vous !

Il faut donc créer une nouvelle page de connexion. C’est là que vous pouvez répondre à la problématique des marketeux. Dans votre template, dupliquez votre fichier page.php que vous pouvez renommer en page-login.php par exemple.

Transformez-le en modèle de page en ajoutant la mention suivante.

<?php
/**
 * Template Name: Page de login
 *
 */
 ?>

Et pour aller au plus simple, ajoutez cette petite fonction de connexion dans le corps de votre page.

<?php  if (is_user_logged_in()) { //Je vérifie si je suis déjà connecté
	 echo 'Vous &ecirc;tes connect&eacute;'; //Si oui, ce message s'affiche
}  else { //Sinon j'appelle le formulaire de connexion
	wp_login_form();
} ?>

Dans l’administration du site, il vous rester à créer une page utilisant ce modèle et lui appliquer l’URL que vous souhaitez.

Je reste très sobre en appelant le formulaire par défaut car ce n’est pas l’objet de cet article. Il faut aussi gérer l’oubli de mot de passe, la création de compte etc. Il y a tellement à ajouter que nous verrons cela dans un autre article. Enfin, il faut surtout pas oublier de désindexer cette page via une balise <meta name= »robots » content= »index,follow » /> que vous faites apparaître uniquement lorsque vous êtes sur le bon ID de la page par exemple (il ne faudrait pas bloquer tout votre site auprès des moteurs de recherche). Ne le faites pas avec robots.txt, un robot un peu malin ou un utilisateur humain pourraient trouver l’url de votre page login que vous avez intelligemment nommé autrement qu’avec login. Genre http://www.webigon.fr/monurlmysterequetunetrouverasjamais. 😉

Une alternative pour le petit blogueur

S’il n’y a pas de notions d’enregistrement pour les visiteurs, vous pouvez aller au plus simple. Ne créons pas de modèle de page, glissons juste un paramètre dans l’URL de wp-login.php qui, lorsque ce paramètre contient la bonne valeur, affiche le formulaire de login comme si de rien n’était. Dans l’exemple ci-dessous, je surveille le paramètre nommé « pass_parameter », puis je vérifie que la valeur associée est bien « lesamediausoleil ». Dans ce cas, je ne fais plus rien de ma fonction.

J’aurai ainsi une URL ressemblant à wp-login.php?pass_parameter=lesamediausoleil ou &pass_parameter=lesamediausoleil si d’autres paramètres sont insérés (telle que la redirection vers une page).

<?php function disable_wplogin() {
//Je crée une variable $pass_parameter avec la valeur de mon paramètre dans l'URL si ce dernier existe.
	$pass_parameter = ( empty($_GET['pass_parameter']) ? '' : $_GET['pass_parameter']); //Remarquez que je le fais via un opérateur ternaire ?: pour simplifier le code.
//Si cette valeur ne correspond pas à la valeur attendue, j'affiche l'erreur.
	 if($pass_parameter!='lesamediausoleil') { ?>
    <div class="login">
    <div id="login">
		<a href="http://www.webigon.fr" title="Webigon.fr" style="text-align:center;width:100%;display:block;">[VOTRE_LOGO]</a>
	<div id="login_error">Webigon se fiche pas mal des comptes enregistrés. Il est inutile de tenter de vous connecter.</div>
	<p id="backtoblog"><a href="http://www.webigon.fr/" title="Êtes-vous perdu(e)&nbsp;?">← Retour sur webigon.fr</a></p>
	</div>
    </div>
    <?php
     die;
	} 
/*
L'accolade précédant ce commentaire signifie la fin de  if($pass_parameter!='lesamediausoleil').
La fonction  die est bien placée avant sinon le ciel nous tomberait sur la tête !
*/
}
add_action('login_head', 'disable_wplogin'); ?>

Le tour est joué, utilisez n’importe quel paramètre sans caractères spéciaux. Du côté de la valeur, vous pouvez y aller, un mot de passe comme « 8jcZ^Pé47àm@=7[T5Lj<kN] » fonctionnera. Interdiction en revanche d’utiliser le point d’interrogation (?) et l’esperluette (&) qui servent à insérer les paramètres dans une URL.

L’avantage avec cette méthode est que vous vous libérez des notions de référencement puisque vous continuer à passer par wp-login.php. Cela vous permet aussi de garder sous le coude les formulaires d’enregistrement et d’oubli de mot de passe (qui demanderont aussi votre nouveau paramètre). Reste à avoir des rédacteurs de confiance dans le cas où vous ne géreriez pas votre site seul.

C’est en place sur Webigon, il me reste à la mettre en production sur jeuxpcmag. Si vous avez des questions ou des commentaires, ça se passe ci-dessous. 😉

Commentaires

  1. Salut, je viens de regarder ton site jeuxpcmag.com pourquoi n’utilise tu pas ton code.
    que pose-t-il comme problème à l’usage avec des utilisateur (inscrits).
    Merci pour le tuto, la piste de recherche est super sympa

    1. Aucun problème, mon site jeuxpcmag s’inscrit dans un tunnel de développement assez long pour moi. Tu peux en revanche retrouver une version complète de cette piste sur https://lesconnards.fr

      Sur ce site, j’ai créé une page d’édition de profil, de connexion et d’enregistrement totalement dédiés. Et j’ai des utilisateurs inscrits.

      1. Cool lol, petite question le lien admin et login pointes vers connexion… si le but est de lutter contre les attaque de force brute pourquoi faire pointer les liens habituel de wordpress vers connexion… car du coup les robots trouvent automatiquement la page ???

      2. Merci pour les info. Pour éviter les attaques force brute, je suppose qu’il faut éviter de faire pointer /admin /login /wp-login /wp-login.php vers la nouvelle page « Connexion » ???

        Je viens de découvrir ton site, feras tu d’autres tuto…

        1. Tu as totalement raison, ce n’était pas mon besoin sur lesconnards.fr. Je protège via fail2ban. Je te parlais de cet exemple qui montre concrètement la gestion avec des utilisateurs. Là je mets à disposition un code qu’il faut adapter selon tes propres besoins. Et contre les robots, il faut bien penser à mettre une url sans logique sémantique si l’objectif est d’éviter les attaques par force brute, ta page de connexion ne doit pas se nommer login, connexion, déconnecter, enregistrement, etc.

  2. Salut, je reviens sur ton script (car il a le mérite d’être simple et efficace).

    Je voudrais après avec tué wp-login… afficher ma page principale (home ou autre)… mais sans redirection c’est possible ?

    Car sur les téléphones quand on fait une redirection cela l’indique dans l’url…

  3. Redirection dans l’url du style ?redirect_to=https%3A%2F%2F… c’est pas top.

    Comment éviter une redirection pour afficher la page principale ? (ou redirection propre lol).

    1. Salut Scam,

      Il y a forcément redirection, mais dans ton cas, n’utilise pas « die » mais « exit » et avant l’appel d’exit, tu fais une redirection « propre » via wp_redirect($url) où $url est celui de ta homepage, tu peux aussi exploiter la fonction intégrée home_url().

      Au final, tu auras quelque chose du style :
      function disable_wplogin() {
      wp_redirect(home_url());
      exit;
      }

      A vérifier, le header de wp-login.php sera déjà chargé, donc ça ne va peut-être pas marcher. Soit tu te prends pas la tête et tu fais un redirect permanant en javascript. Soit tu modifies add_action(‘login_head’, ‘disable_wplogin’). Il faut remplacer ‘login_head’ par un autre hook mais pas sûr qu’il existe. Bref, tente déjà comme ça pour voir.

    1. Tu l’auras toujours. Le paramètre redirect_to est utilisé par wordpress pour te rediriger après login. Mais au final, c’est quoi ton problème avec ce paramètre ?

      Le sujet de l’article est de bloquer la possibilité de se connecter via wp-login.php, rien de plus. Je crois que ce que tu recherches n’est pas uniquement cela, je me trompe ?