Un anti brute-force léger et rapide
Formation
En Semi-présenciel Paris
Description
-
Typologie
Formation
-
Méthodologie
En semi-présentiel
-
Lieu
Paris
Grâce à cette formation vous pourrez acquérir les connaissances nécessaires qui vous permettrons d’ajouter des compétences à votre profil et obtenir de solides aptitude qui vous offriront de nombreuses opportunités professionnelles.
Les sites et dates disponibles
Lieu
Date de début
Date de début
Les Avis
Le programme
Vous avez sécurisé votre site au maximum, exterminé les failles, protégé vos pages... mais la menace de la force brute pèse sur vous ? :euh:
La force brute consiste à tester à l'aide d'un script des tas de mots de passe dans vos formulaires, pour accéder à votre administration par exemple (ce qui est plutôt désagréable, je vous l'accorde).
Mettre en place un anti brute-force en utilisant la base de données est la solution la plus souvent envisagée sur les forums du Site du Zér0. Cela fonctionne parfaitement, mais c'est gourmand (une requête pour chaque identification ratée, sans compter les vérifications par la suite o_O ).
Dans ce tutoriel, nous allons nous pencher sur une solution plus rapide et qui ne demandera pas l'intervention d'une base de données : un anti brute-force qui fonctionne grâce à des fichiers.
Est-ce bien utile ?Mais, comment peut-on contrer une attaque de ce genre ?
C'est très simple : le pirate va essayer plein de mots de passe, mais vraiment beaucoup, car on ne trouve pas un mot de passe aussi facilement.
Le script du pirate va essayer des centaines de mots de passe formés, soit aléatoirement, soit piochés dans une liste qu'il aura téléchargé ou préétabli. On appelle cette seconde option une attaque par dictionnaire.
Mais alors, si c'est aussi compliqué et long pour trouver un mot de passe, à quoi ça sert de s'embêter avec un système de protection ? o_O
C'est vrai, certains développeurs vous diront qu'il est inutile de mettre en place ce genre de protection, car, en théorie, il est très difficile de trouver un mot de passe complexe.
Certes, un pirate mettrait des mois à trouver un mot de passe complexe comme celui-ci : 4gf/ki-2KL@[23]. Mais qu'en est-il des mots de passe de vos membres ?
Il y a peu de chances de voir des membres utiliser ce genre de mot de passe ; certains mettront certainement des mots de passe basiques comme un prénom ou le nom de leur héros préféré. Et là, une attaque par dictionnaire le trouvera en peu de temps. Un pirate pourra alors s'amuser avec le compte d'un de vos membres et, croyez-moi, c'est sur vous et sur la réputation du site que ça retombera.
Une des solutions consiste simplement à forcer les gens à entrer un mot de passe très complexe lors de leur inscription. Cette solution n'est pas forcement celle à adopter : moins les visiteurs auront d'obstacles lors de leur inscription et de leur navigation, et meilleure sera le souvenir qu'ils garderont de votre site web.
Principe de baseIci, on ne va pas utiliser de base de données pour notre script (trop lent et trop gourmand pour cette utilisation) mais des fichiers.
Oh, mais tu oublies les sessions et les cookies !
Non, je ne les ai pas oubliés ; c'est en effet une solution. Néanmoins, il y a un petit souci avec eux : il suffit de vider ses cookies pour pouvoir recommencer à essayer des mots de passe. De plus, vous allez voir qu'utiliser des fichiers n'est pas plus difficile !
Le principe est simple : si quelqu'un tente de s'identifier avec un mauvais mot de passe, on crée un fichier nommé pseudodumembre.tmp qui contiendra la date du jour et le nombre de tentatives.
Et c'est tout, ce sont les seules informations dont on aura besoin.
À chaque fois qu'une personne tentera de s'identifier, il suffira de regarder si un fichier ayant comme nom son pseudo existe. S'il a atteint le quota de mauvais mots de passe journalier, on bloque l'identification.
À vos claviers, c'est partiPour pouvoir appliquer ce tutoriel, vous devez connaître les bases de la gestion de fichiers en PHP.
Commençons par le plus simple : la création du dossier qui contiendra tous les fichiers générés par le script anti brute-force. Vous pouvez créer le dossier où vous le souhaitez et le nommer à votre goût. Le tutoriel sera basé sur un dossier antibrute placé à la racine du site (si vous êtes sous linux, pensez à chmoder le dossier en 777).
C'est fait ? Parfait, on peut continuer ! :)
Voici le script d'identification (ultra-simplifié) sur lequel nous allons nous baser pour créer l'anti brute-force. Évidemment celui-ci est une pure invention, vous pouvez appliquer ce tutoriel sur vos propres scripts.
<?php // Si on tente de s'identifier if(!empty($_POST['pseudo']) AND !empty($_POST['motdepasse'])) { $verifications = mysql_query('SELECT pseudo, motdepasse FROM membres WHERE pseudo = \''.mysql_real_escape_string($_POST['pseudo']).'\' '); $data_verif = mysql_fetch_assoc($verifications); // Si le pseudo existe bien if(!empty($data_verif['pseudo'])) { // Si le mot de passe est bon if($data_verif['motdepasse'] == trim($_POST['motdepasse'])) { //------------------------------------------------ // Ici Votre script qui identifie le membre //------------------------------------------------ } // Si le mot de passe est faux else { echo 'Mot de passe incorrect'; } } // Si le pseudo n'existe pas else { echo 'Pseudonyme incorrect'; } } ?>Jusque là, normalement, ça vous est tout à fait familier. Commençons les choses sérieuses.
Avant tout, on va vérifier l'existence d'un fichier indiquant des tentatives d'identification.
Si on trouve un fichier créé dans la journée, on récupère les informations qu'il contient ; on saura alors combien de tentatives il reste au visiteur en question.
On commence par regarder si le fichier existe grâce à la fonction file_exists().
Si la réponse est positive, on ouvre le fichier avec fopen() puis on récupère le contenu du fichier dans une variable avec fgets().
Il faut aussi que l'on fasse en sorte que le fichier soit créé s'il n'existe pas encore, ou mis à jour si la date est dépassée. Pour cela, on va donner naissance à une variable $existence_ft en cas de retour négatif de la fonction file_exists() ou de mauvaise date.
Mais pourquoi ne pas créer / mettre à jour le fichier tout de suite au lieu de créer cette variable ?
Si on créait le fichier tout de suite, il faudrait forcement le faire en se basant sur le pseudonyme entré dans le formulaire par le pirate. On se retrouverait donc avec un fichier par pseudo même s'il n'existe pas ; le pirate pourrait alors faire planter le serveur en essayant des milliers de pseudos bidons ! On créera donc le fichier uniquement si le pseudonyme existe dans la base de données. Comme cela, il y aura un maximum d'un fichier par membre. D'où l'utilité de cette variable dont on vérifiera l'existence au moment venu pour éviter de regarder une deuxième fois si le fichier existe.
Pour ce qui est de la mise à jour de la date, à quoi bon consommer des ressources pour changer la date alors que le membre ne va peut être pas se tromper ? Celui-ci peut très bien s'identifier chaque jour sans problème, ce serait du gaspillage de ressources de s'amuser à changer le contenu du fichier pour rien.
Dans un premier temps, on met $existence_ft à 0 si le fichier n'existe pas du tout.
<?php // Si on tente de s'identifier if(!empty($_POST['pseudo']) AND !empty($_POST['motdepasse'])) { // On initialise $existence_ft $existence_ft = ''; // Si le fichier existe, on le lit if(file_exists('antibrute/'.$_POST['pseudo'].'.tmp')) { // On ouvre le fichier $fichier_tentatives = fopen('antibrute/'.$_POST['pseudo'].'.tmp', 'r+'); // On récupère son contenu dans la variable $infos_tentatives $contenu_tentatives = fgets($fichier_tentatives); } // Si le fichier n'existe pas encore, on met la variable $existence_ft à 1 et on met les $tentatives à 0 else { $existence_ft = 1; $tentatives = 0; } $verifications = mysql_query('SELECT pseudo, motdepasse FROM membres WHERE pseudo = \''.mysql_real_escape_string($_POST['pseudo']).'\' '); $data_verif = mysql_fetch_assoc($verifications); // Si le pseudo existe bien if(!empty($data_verif['pseudo'])) { // Si le mot de passe est bon if($data_verif['motdepasse'] == trim($_POST['motdepasse'])) { //------------------------------------------------ // Ici Votre script qui identifie le membre //------------------------------------------------ } // Si le mot de passe est faux else { echo 'Mot de passe incorrect'; } } // Si le pseudo n'existe pas else { echo 'Pseudonyme incorrect'; } } ?>Si le fichier existe on obtient son contenu dans la variable $contenu_tentatives, il ne nous reste plus qu'à traiter ces informations pour pouvoir les utiliser. La fonction explode() va nous permettre de couper en morceaux le contenu du fichier selon le séparateur de notre choix. Ici le point virgule ( ; ).
<?php // Si on tente de s'identifier if(!empty($_POST['pseudo']) AND !empty($_POST['motdepasse'])) { // On initialise $existence_ft $existence_ft = ''; // Si le fichier existe, on le lit if(file_exists('antibrute/'.$_POST['pseudo'].'.tmp')) { // On ouvre le fichier $fichier_tentatives = fopen('antibrute/'.$_POST['pseudo'].'.tmp', 'r+'); // On récupère son contenu dans la variable $infos_tentatives $contenu_tentatives = fgets($fichier_tentatives); // On découpe le contenu du fichier pour récupérer les informations $infos_tentatives = explode(';', $contenu_tentatives); } // Si le fichier n'existe pas encore, on met la variable $existence_ft à 1 et on met les $tentatives à 0 else { $existence_ft = 1; $tentatives = 0; } $verifications = mysql_query('SELECT pseudo, motdepasse FROM membres WHERE pseudo = \''.mysql_real_escape_string($_POST['pseudo']).'\' '); $data_verif = mysql_fetch_assoc($verifications); // Si le pseudo existe bien if(!empty($data_verif['pseudo'])) { // Si le mot de passe est bon if($data_verif['motdepasse'] == trim($_POST['motdepasse'])) { //------------------------------------------------ // Ici Votre script qui identifie le membre //------------------------------------------------ } // Si le mot de passe est faux else { echo 'Mot de passe incorrect'; } } // Si le pseudo n'existe pas else { echo 'Pseudonyme incorrect'; } } ?>Le contenu de notre fichier se présente de cette manière :
01/10/2008;0
La fonction explode() a donc créé un tableau contenant tout d'abord la date puis le nombre de tentatives.
$infos_tentatives[0] contient la date.
$infos_tentatives[1] contient le nombre de tentatives.
La première chose que l'on va faire, c'est comparer la date du jour à la date que l'on vient de récupérer dans le fichier. Si la date est identique, le fichier est encore valide et on va utiliser le nombre de tentatives qu'il contient. Si au contraire la date ne correspond pas, on met la fameuse variable $existence_ft à 1.
On en profite pour créer la variable $tentatives que l'on utilisera pour savoir si le quota est dépassé ou non.
<?php // Si on tente de s'identifier if(!empty($_POST['pseudo']) AND !empty($_POST['motdepasse'])) { // On initialise $existence_ft $existence_ft = ''; // Si le fichier existe, on le lit if(file_exists('antibrute/'.$_POST['pseudo'].'.tmp')) { // On ouvre le fichier $fichier_tentatives = fopen('antibrute/'.$_POST['pseudo'].'.tmp', 'r+'); // On récupère son contenu dans la variable $infos_tentatives $contenu_tentatives = fgets($fichier_tentatives); // On découpe le contenu du fichier pour récupérer les informations $infos_tentatives = explode(';', $contenu_tentatives); // Si la date du fichier est celle d'aujourd'hui, on récupère le nombre de tentatives if($infos_tentatives[0] == date('d/m/Y')) { $tentatives = $infos_tentatives[1]; } // Si la date du fichier est dépassée, on met le nombre de tentatives à 0 et $existence_ft à 2 else { $existence_ft = 2; $tentatives = 0; // On met la variable $tentatives à 0 } } // Si le fichier n'existe pas encore, on met la variable $existence_ft à 1 et on met les $tentatives à 0 else { $existence_ft = 1; $tentatives = 0; } $verifications = mysql_query('SELECT pseudo, motdepasse FROM membres WHERE pseudo = \''.mysql_real_escape_string($_POST['pseudo']).'\' '); $data_verif = mysql_fetch_assoc($verifications); // Si le pseudo existe bien if(!empty($data_verif['pseudo'])) { // Si le mot de passe est bon if($data_verif['motdepasse'] == trim($_POST['motdepasse'])) { //------------------------------------------------ // Ici Votre script qui identifie le membre //------------------------------------------------ } // Si le mot de passe est faux else { echo 'Mot de passe incorrect'; } } // Si le pseudo n'existe pas else { echo 'Pseudonyme incorrect'; } } ?>Pfiou, ça avance. Maintenant on possède deux variables qui nous fournissent toutes les informations dont on a besoin pour la suite. Il ne reste que deux choses à faire avant que le script soit fonctionnel :
Couper l'accès à l'identification dès que le quota de tentatives a été atteint.
Mettre à jour le fichier à chaque fois qu'un mauvais mot de passe est envoyé.
La première tâche est très simple à réaliser, on a déjà le nombre de tentatives dans une variable. Tout ce qu'il reste à faire c'est de le comparer à notre quota.
<?php // Si on tente de s'identifier if(!empty($_POST['pseudo']) AND !empty($_POST['motdepasse'])) { // On initialise $existence_ft $existence_ft = ''; // Si le fichier existe, on le lit if(file_exists('antibrute/'.$_POST['pseudo'].'.tmp')) { // On ouvre le fichier $fichier_tentatives = fopen('antibrute/'.$_POST['pseudo'].'.tmp', 'r+'); // On récupère son contenu dans la variable $infos_tentatives $contenu_tentatives = fgets($fichier_tentatives); // On découpe le contenu du fichier pour récupérer les informations $infos_tentatives = explode(';', $contenu_tentatives); // Si la date du fichier est celle d'aujourd'hui, on récupère le nombre de tentatives if($infos_tentatives[0] == date('d/m/Y')) { $tentatives = $infos_tentatives[1]; } // Si la date du fichier est dépassée, on met le nombre de tentatives à 0 et $existence_ft à 2 else { $existence_ft = 2; $tentatives = 0; // On met la variable $tentatives à 0 } } // Si le fichier n'existe pas encore, on met la variable $existence_ft à 1 et on met les $tentatives à 0 else { $existence_ft = 1; $tentatives = 0; } // S'il y a eu moins de 30 identifications ratées dans la journée, on laisse passer if($tentatives < 30) { $verifications = mysql_query('SELECT pseudo, motdepasse FROM membres WHERE pseudo = \''.mysql_real_escape_string($_POST['pseudo']).'\' '); $data_verif = mysql_fetch_assoc($verifications); // Si le pseudo existe bien if(!empty($data_verif['pseudo'])) { // Si le mot de passe est bon if($data_verif['motdepasse'] == trim($_POST['motdepasse'])) { //------------------------------------------------ // Ici Votre script qui identifie le membre //------------------------------------------------ } // Si le mot de passe est faux else { echo 'Mot de passe incorrect'; } } // Si le pseudo n'existe pas else { echo 'Pseudonyme incorrect'; } } // S'il y a déjà eu 30 tentatives dans la journée, on affiche un message d'erreur else { echo 'Trop de tentatives d\'authentification aujourd\'hui'; } } ?>La seconde sera légèrement plus ardue. Si le fichier n'existe pas encore, on le crée et on indique à l'intérieur qu'il y a eu une tentative. Mais...
Un anti brute-force léger et rapide
