Aller au contenu

Formulaire de login : éviter les attaques par dictionnaire


kyotoo

Sujets conseillés

Bonjour

Afin de protéger un formulaire de login, j'ai mis en place un système de blacklist IP :

Au bout de 3 essais infructueux, l'adresse IP est bloquée (bouton submit désactivé)

Est-ce une méthode efficace pour freiner les attaques par dictionnaire ?

A part le captcha (j'en ai lu des vertes et des pas mûres sur le sujet), connaissez-vous d'autres méthodes ?

Note : le formulaire de login est envoyé via la requête POST

Christophe

Modifié par kyotoo
Lien vers le commentaire
Partager sur d’autres sites

A priori si le bouton submit est désactivé la requête POST ne peut pas être envoyée, à moins que l'utilisateur ne rafraichisse la page, auquel cas c'est la même combinaison login/pass qui est envoyée (et non une autre combinaison permettant de faire du brute force)

Aurais-je été enduit d'erreur ?

Lien vers le commentaire
Partager sur d’autres sites

N'oublie pas que l'IP d'un ordinateur change à chaque connection. Il suffit de se déconnecter et de se reconnecter pour redevenir "clean". Je ne sais pas s'il y a moyen de récupérer l'addresse IP interne du PC, qui ne change jamais.

EDIT:

Pourquoi te fatiges-tu à déconnecter le bouton submit, tu n'as qu'à ne plus afficher le formulaire et afficher à la place un petit message.

Modifié par theprogrammeur
Lien vers le commentaire
Partager sur d’autres sites

L'IP ne change pas si souvent (à part chez aol d'après mon expérience) et mon but est surtout d'éviter les scripts robotiques qui envoient des centaines de tentatives par minute.

L'utilisateur "de base" va vite se fatiguer au bout d'une dizaine d'essais infructueux, surtout s'il doit changer d'IP à chaque multiple de 3 :)

Quant à la désactivation du bouton je me suis posé la question.

Je pourrais en effet afficher un message xyz.

Lien vers le commentaire
Partager sur d’autres sites

En ce qui concerne les scripts automatiques, il ne vont pas cliquer sur le bouton, mais directement envoyer les données POST à la page.

La meilleure solution, serait de bloquer l'exécution de ton script, pas juste l'affichage du bouton.

Lien vers le commentaire
Partager sur d’autres sites

A priori si le bouton submit est désactivé la requête POST ne peut pas être envoyée, à moins que l'utilisateur ne rafraichisse la page, auquel cas c'est la même combinaison login/pass qui est envoyée (et non une autre combinaison permettant de faire du brute force)

Aurais-je été enduit d'erreur ?

Si on a envie de t'ennuyer, on peut forcer l'envoi du formulaire par script ou en modifiant ton code HTML (donc en recréant un bouton submit).

Lien vers le commentaire
Partager sur d’autres sites

Bonjour,

J'ai récemment travaillé avec un spécialiste en sécurité sur la partie identification/authentification d'un site Internet.

D'après lui, un moyen simple pour, non pas empécher, mais freiner les attaques par force brute ou dictionnaire c'est d'empêcher l'identification pour un temps donné après n tentatvies. Par exemple, au bout de 3 tentatives, tu bloque l'identification (pour un login/IP spécifique bien entendu) pour 10min. A la tentative suivante, tu bloque pour 1/2 heure, puis pour 1h, etc... Extrêmement efficace.

Lien vers le commentaire
Partager sur d’autres sites

Pour ma part je bloque 24h, après 3 tentatives par IP, et ce quelque soit le login.

Si c'est un utilisateur légitime, il a la possibilité de regénérer un mot de passe, ce qui a pour effet de débloquer son IP

Pour regénérer un mot de passe il faut connaitre l'adresse email associée au compte qui a subi une tentative d'usurpation.

Pas simple comme histoire

Lien vers le commentaire
Partager sur d’autres sites

Ce genre d'attaque se fait le plus régulièrement par sql injection, la personne qui va vouloir te "brute-forcer" ne va pas soumettre 10000 fois ton formulaire avant de trouver un mot de passe.

Je ne vais pas décrire exactement le procédé le plus courant, mais la recherche s'affine peu à peu par les clauses LIKE ... (pour le login et le mot de passe).

Un bon moyen pour s'en prémunir est d'une part de stocker des mots de passes cryptés avec la fonction md5 de php et de comparer des mots de passes cryptés lors de la tentative de connexion. (bien que n'etant pas un expert en cryptologie, md5 n'est pas réversible contrairement à ce que j'ai pu lire ici et là, à moins que cette fonction ait un nom malheureux en php).

La moralité est qu'il faut t'inquiéter surtout pour ta requete avant de t'inquieter pour les ips qui sont modifiables.

Modifié par hcplayer
Lien vers le commentaire
Partager sur d’autres sites

Ce que dit hcplayer est exact.

Effectivement, crypter le mot de passe est une bonne solution.

Pour limiter les risques d'injection SQL, il faut séparer l'identification de l'authentification.

L'erreur courante est de chercher un couple login/mot de passe dans la même requête. Pas bon !

Il vaut mieux procéder comme suit :

Chercher dans la base, les infos sur le login saisi (SELECT * FROM users WHERE login='').

Si la requête renvoie 0 (login inexistant) ou plus d'1 résultat (louche!) ===> se casser vite fait !

Si la requete renvoie 1 résultat (on a trouvé le login) alors comparer le mot de passe saisie avec celui stocké dans la base de donnée. Le tout crypté bien entendu. Si la comparaison est ok, alors on autorise l'accès.

Lien vers le commentaire
Partager sur d’autres sites

Merci pour les précisions.

En fait je me suis basé sur cet article :

http://www.olate.co.uk/articles/185

Non seulement les mots de passe sont chiffrés, mais chiffrés de plus avec un "salt"

En ce qui concerne l'identification et l'authentification elle se fait bien en 2 étapes :

function user_login($username, $password)
{
// Try and get the salt from the database using the username
$query = "select salt from user where username='$username' limit 1";
$result = mysql_query($query);

if (mysql_num_rows($result) > 0)
{
// Get the user
$user = mysql_fetch_array($result);

// Using the salt, encrypt the given password to see if it
// matches the one in the database
$encrypted_pass = md5(md5($password).$user['salt']);

// Try and get the user using the username & encrypted pass
$query = "select userid, username from user where username='$username' and password='$encrypted_pass'";
$result = mysql_query($query);

if (mysql_num_rows($result) > 0)
{
$user = mysql_fetch_array($result);

// Now encrypt the data to be stored in the session
$encrypted_id = md5($user['userid']);
$encrypted_name = md5($user['username']);

// Store the data in the session
$_SESSION['userid'] = $userid;
$_SESSION['username'] = $username;
$_SESSION['encrypted_id'] = $encrypted_id;
$_SESSION['encrypted_name'] = $encrypted_name;

// Return ok code
return 'Correct';
}
else
{
return 'Invalid Password';
}
}
else
{
return 'Invalid Username';
}
}

Un bon moyen pour s'en prémunir est d'une part de stocker des mots de passes cryptés avec la fonction md5 de php et de comparer des mots de passes cryptés lors de la tentative de connexion.

Cependant, le fait de chiffrer les mots de passe n'empêche pas les tentatives de connexion, car dans le formulaire on saisit le mot de passe en clair. Celui-ci n'est chiffré que sur le serveur.

En résumé il n'y a pas de méthode miracle, mais j'aimerais bien en savoir plus sur les clauses LIKE

Si vous avez des liens ou infos... :)

Lien vers le commentaire
Partager sur d’autres sites

C'est amusant que la chaîne $username ne soit pas protégée (avec mysql_escape_string par exemple pour mysql) avant d'être utilisée dans la requête.

Après, je ne vois pas trop comment (et où surtout) est initialisée la variable : '$userid'.

De plus, les informations contenus dans les sessions ne sont à priori pas accessible au client et ne se balade pas plus sur le réseau ? Euh sinon j'aimerai bien savoir comment.

Donc pourquoi les crypter ?

Lien vers le commentaire
Partager sur d’autres sites

C'est amusant que la chaîne $username ne soit pas protégée (avec mysql_escape_string par exemple pour mysql) avant d'être utilisée dans la requête.

En effet, car elle est protegée au moment où je fais appel à la fonction : je passe les arguments après la moulinette suivante:

function safe_input($input) 
{

if (!get_magic_quotes_gpc()) {

$input = addslashes($input);

}

$input = trim(htmlspecialchars($input));
return $input;

}

Après, je ne vois pas trop comment (et où surtout) est initialisée la variable : '$userid'.

C'est un oubli dans le code du tutorial que je n'avais pas vu, merci

De plus, les informations contenus dans les sessions ne sont à priori pas accessible au client et ne se balade pas plus sur le réseau ? Euh sinon j'aimerai bien savoir comment.

Cette partie est mystique pour moi...

Lien vers le commentaire
Partager sur d’autres sites

Après je sais pas si c'est important, je ne m'y connais pas assez pour juger de cela. Je me dis juste que si par défaut la fonction le fait et si on nous recommande de le faire, c'est que cela doit servir un peu. ;o)

Lien vers le commentaire
Partager sur d’autres sites

Veuillez vous connecter pour commenter

Vous pourrez laisser un commentaire après vous êtes connecté.



Connectez-vous maintenant
×
×
  • Créer...