Question

Je recherchais un algorithme efficace qui puisse me donner une idée précise de la force d'un mot de passe.

J'ai constaté que plusieurs sites Web différents utilisent plusieurs algorithmes différents, car différents niveaux de force de mot de passe sont attribués sur différents sites Web.

Était-ce utile?

La solution

Cela a pris de l’ampleur dans mon cerveau en général de bonnes pratiques pour travailler avec des mots de passe en PHP / MySQL. Les idées présentées ici ne sont généralement pas les miennes, mais le meilleur de ce que j'ai trouvé à ce jour.


Assurez-vous que vous utilisez SSL pour toutes les opérations impliquant des informations utilisateur. Toutes les pages impliquant ces formulaires doivent vérifier si elles sont appelées via HTTPS et refuser de fonctionner autrement.

Vous pouvez éliminer la plupart des attaques en limitant simplement le nombre de connexions échouées autorisées.

Autorisez les mots de passe relativement faibles, mais enregistrez le nombre de connexions échouées par utilisateur et exigez une vérification captcha ou un mot de passe par courrier électronique si vous le dépassez. Je règle mon maximum d'échecs à 5.

La présentation des échecs de connexion à l'utilisateur doit être soigneusement pensée afin de ne pas fournir d'informations aux attaquants. Un échec de connexion dû à un utilisateur inexistant devrait renvoyer le même message qu'un échec de connexion dû à un mot de passe incorrect. Envoyer un autre message permettra aux attaquants de déterminer des identifiants de connexion valides.

Assurez-vous également de renvoyer exactement le même message en cas d'échec d'un trop grand nombre de connexions avec un mot de passe valide et d'un échec avec trop de connexions et d'un mot de passe incorrect. En fournissant un message différent, les pirates pourront déterminer les mots de passe valides des utilisateurs. Un nombre non négligeable d’utilisateurs, lorsqu'ils sont forcés de réinitialiser leur mot de passe, le remettra simplement à ce qu’il était.

Malheureusement, limiter le nombre de connexions autorisées par adresse IP n’est pas pratique. Plusieurs fournisseurs tels qu'AOL et la plupart des entreprises procèdent par proxy à leurs requêtes Web. L'imposition de cette limite éliminera effectivement ces utilisateurs.


J'ai constaté que la vérification des mots du dictionnaire avant l'envoi était inefficace, car vous devez envoyer un dictionnaire au client en javascript ou envoyer une demande ajax par changement de champ. Je l'ai fait pendant un certain temps et cela a fonctionné, mais je n'ai pas aimé le trafic généré.

La recherche de mots de passe intrinsèquement faibles, moins les mots du dictionnaire, est pratique côté client avec du javascript simple.

Après soumission, je vérifie les mots du dictionnaire et le nom d'utilisateur contenant le mot de passe et inversement côté serveur. De très bons dictionnaires sont facilement téléchargeables et leur test est simple. Un des pièges à cela est que pour tester un mot du dictionnaire, vous devez envoyer une requête à la base de données, qui contient à nouveau le mot de passe. La façon dont j'ai réussi à éviter cela consistait à chiffrer mon dictionnaire au préalable avec un chiffrement simple et à positionner finement SALT, puis à tester le mot de passe chiffré. Pas idéal, mais meilleur que le texte brut et uniquement sur le réseau pour les utilisateurs de vos machines physiques et de votre sous-réseau.

Une fois que vous êtes satisfait du mot de passe, ils ont choisi de le chiffrer avec PHP, puis de le stocker. La fonction de cryptage de mot de passe suivante n'est pas mon idée non plus, mais résout un certain nombre de problèmes. Le cryptage dans PHP empêche les utilisateurs d'un serveur partagé d'intercepter vos mots de passe non cryptés. Ajouter quelque chose par utilisateur qui ne changera pas (j'utilise l'email, car c'est le nom d'utilisateur de mes sites) et ajouter un hachage (SALT est une chaîne courte et constante que je change par site) augmente la résistance aux attaques. Parce que SALT est situé dans le mot de passe et que le mot de passe peut avoir n'importe quelle longueur, il devient presque impossible d'attaquer cela avec une table arc-en-ciel. Autrement, cela signifie également que les personnes ne peuvent pas modifier leur adresse électronique et que vous ne pouvez pas modifier SALT sans invalider le mot de passe de chacun.

EDIT: je recommanderais maintenant d'utiliser PhPass au lieu de ma propre fonction ici, ou tout simplement Oubliez complètement les connexions des utilisateurs et utilisez plutôt OpenID .

function password_crypt($email,$toHash) {
  $password = str_split($toHash,(strlen($toHash)/2)+1);
  return hash('sha256', $email.$password[0].SALT.$password[1]); 
}

Mesureur de mot de passe côté client Jqueryish. La cible devrait être un div. Sa largeur changera entre 0 et 100 et la couleur de fond changera en fonction des classes indiquées dans le script. Encore une fois principalement volé d'autres choses que j'ai trouvées:

$.updatePasswordMeter = function(password,username,target) {
$.updatePasswordMeter._checkRepetition = function(pLen,str) {
res = ""
for ( i=0; i<str.length ; i++ ) {
    repeated=true;
    for (j=0;j < pLen && (j+i+pLen) < str.length;j++)
        repeated=repeated && (str.charAt(j+i)==str.charAt(j+i+pLen));
    if (j<pLen) repeated=false;
    if (repeated) {
        i+=pLen-1;
        repeated=false;
    }
    else {
        res+=str.charAt(i);
    };
};
return res;
};
var score = 0;
var r_class = 'weak-password';
//password < 4
if (password.length < 4 || password.toLowerCase()==username.toLowerCase()) { 
target.width(score + '%').removeClass("weak-password okay-password good-password strong-password"
).addClass(r_class);
  return true;
} 
//password length
score += password.length * 4;
score += ( $.updatePasswordMeter._checkRepetition(1,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(2,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(3,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(4,password).length - password.length ) * 1;
//password has 3 numbers
if (password.match(/(.*[0-9].*[0-9].*[0-9])/))  score += 5; 

//password has 2 symbols
if (password.match(/(.*[!,@,#,$,%,^,&,*,?,_,~].*[!,@,#,$,%,^,&,*,?,_,~])/)) score += 5; 

//password has Upper and Lower chars
if (password.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/))  score += 10; 

//password has number and chars
if (password.match(/([a-zA-Z])/) && password.match(/([0-9])/))  score += 15; 
//
//password has number and symbol
if (password.match(/([!,@,#,$,%,^,&,*,?,_,~])/) && password.match(/([0-9])/))  score += 15; 

//password has char and symbol
if (password.match(/([!,@,#,$,%,^,&,*,?,_,~])/) && password.match(/([a-zA-Z])/))  score += 15; 

//password is just a nubers or chars
if (password.match(/^\w+$/) || password.match(/^\d+$/) )  score -= 10; 

//verifing 0 < score < 100
score = score * 2;
if ( score < 0 )  score = 0;
if ( score > 100 )  score = 100;
if (score > 25 ) r_class = 'okay-password';
if (score > 50  ) r_class = 'good-password';
if (score > 75 ) r_class = 'strong-password';
target.width(score + '%').removeClass("weak-password okay-password good-password strong-password"
).addClass(r_class);
return true;
};

Autres conseils

Fondamentalement, vous voulez prévenir les principaux types d’attaques

  • Attaques du dictionnaire
  • Attaques par force brute

Pour éviter le premier problème, vous souhaitez prendre en compte les mots de passe contenant des mots courants faibles. Pour éviter le second problème, vous souhaitez encourager les mots de passe de longueur raisonnable (plus de 8 caractères sont courants) et avec un jeu de caractères assez volumineux (inclure des lettres, des chiffres et des caractères spéciaux). Si vous considérez que les lettres minuscules et majuscules sont différentes, cela augmente considérablement le jeu de caractères. Toutefois, cela crée un problème de convivialité pour certaines communautés d’utilisateurs. Vous devez donc prendre en compte cet aspect.

Une recherche rapide sur Google a mis au point des solutions tenant compte des attaques par force brute (mot de passe complexe) mais non des attaques par dictionnaire. Indicateur de force de mot de passe PHP de cette liste des vérificateurs de force exécute la vérification côté serveur. Elle peut donc être étendue pour vérifier un dictionnaire.

EDIT:

Au fait ... vous devriez également limiter le nombre de tentatives de connexion par utilisateur. Cela rendra les deux types d'attaques moins probables. Efficace mais non convivial, il consiste à verrouiller un compte après X tentatives infructueuses et à demander la réinitialisation du mot de passe. Une utilisation plus conviviale mais plus difficile consiste à limiter le temps entre les tentatives de connexion. Vous pouvez également exiger CAPTCHA après les premières tentatives de connexion (ce qui est une nécessité pour Stack Overflow après trop de modifications , ou pour les tout nouveaux utilisateurs).

En principe, vous souhaiterez probablement utiliser des expressions régulières pour valider la longueur et la complexité du mot de passe.

Vous trouverez un bon exemple d'utilisation de javascript à cette adresse:

http://marketingtechblog.com/programming/javascript-password-strength/

Comme l'a souligné Daren Schwenke, vous feriez mieux de travailler vous-même sur la sécurité et de ne pas laisser cela entre les mains de l'utilisateur .

Mais il est bon de fournir à l'utilisateur des indices sur la force de son mot de passe, car le meilleur moyen d'obtenir un mot de passe est toujours de générer un impact social .

Vous pouvez donc pirater un petit script côté client qui vérifie la force du mot de passe de l'utilisateur en tant qu'indicateur de courtoisie, en temps réel. Cela ne bloque rien, mais lui donne une bonne sensation de chaleur quand il devient vert: -)

Fondamentalement, vous devez vérifier le sens commun: vérifiez si le mot de passe contient des lettres, des chiffres et des caractères non alphabétiques, en quantité raisonnable.

Vous pouvez pirater votre propre algo très facilement: il suffit de faire 10/10 mark:

  • 0 est un mot de passe de longueur nulle;
  • +2 pour chaque 8 caractères du mot de passe (15 est censé être une longueur sûre);
  • +1 pour l'utilisation d'une lettre, +2 pour l'utilisation de 2 lettres;
  • +1 pour l'utilisation d'un nombre, +2 pour l'utilisation de 2 chiffres;
  • +1 pour l'utilisation de caractères non alphabétiques, +2 pour 2.

Vous n'avez pas besoin de vérifier les mots de passe divins (lettres majuscules, emplacement des caractères spéciaux, etc.), vos utilisateurs ne font pas partie du secteur bancaire / militaire / des services secrets / des films mensuels en python, n'est-ce pas?

Vous pouvez coder cela en une heure sans compétences javascript dingues.

Enfin, validez le mot de passe et déplacez tout le code de sécurité côté serveur. Si vous pouvez déléguer l’authentification (par exemple: identifiant ouvert), c’est encore mieux.

Je ne peux pas penser à un algorithme spécifique pour vérifier la force d'un mot de passe. Ce que nous faisons est que nous définissions plusieurs critères et lorsque le mot de passe respecte un critère, nous ajoutons 1 à son score. Lorsque le mot de passe atteint un seuil, il est fort. Sinon c'est faible.

Vous pouvez définir différents niveaux de résistance si vous avez différentes throeshold, ou vous pouvez définir une valeur différente pour un critère spécifique. Par exemple, si un mot de passe a 5 caractères, on ajoute 1, mais s'il en a 10, on ajoute 2.

voici une liste de critères à vérifier

Longueur (8 à 12 c'est bien, plus c'est mieux) Contient une lettre minuscule Contient une lettre majuscule La lettre majuscule n'est PAS la première. Contient le numéro Contient des symboles le dernier caractère n'est PAS un symbole humain (ex:. ou!) Ne ressemble pas à un mot du dictionnaire. Un crack de mot de passe contient une bibliothèque de substituts de mots et de lettres (comme Library - > L1br @ ry)

J'espère que cela vous aidera.

Ne roulez pas vous-même!

Les experts en cryptographie découragent la cryptographie à la volée pour des raisons qui devraient être évidentes.

Pour les mêmes raisons, il ne faut pas chercher sa propre solution au problème de la mesure de la force d'un mot de passe. c'est vraiment un problème de cryptographie.

Ne vous lancez pas dans la vilaine entreprise de créer des expressions régulières massives à cette fin; vous ne pourrez probablement pas prendre en compte plusieurs facteurs qui influencent la force globale d'un mot de passe.

C'est un problème difficile

Il est extrêmement difficile de mesurer la force d’un mot de passe. Plus je fais de recherches sur ce sujet, plus je réalise qu’il s’agit d’un projet "unidirectionnel". problème; c'est-à-dire qu'on ne peut pas mesurer la "difficulté" (coût de calcul) de la fissuration d’un mot de passe efficacement . Il est plutôt plus efficace de fournir des exigences de complexité et de mesurer la capacité du mot de passe à les respecter.

Lorsque nous considérons le problème de manière logique, un "indice de craquabilité". n'a pas beaucoup de sens, aussi pratique que cela puisse paraître. Il y a tellement de facteurs qui déterminent le calcul, dont la plupart sont liés aux ressources de calcul consacrées au processus de craquage, de sorte que ce n'est pas pratique.

Imaginez que John the Ripper (ou un outil similaire) soit opposé au mot de passe en question; cela peut prendre des jours pour déchiffrer un mot de passe correct, des mois pour craquer un bon mot de passe et jusqu'à ce que le soleil disparaisse pour faire craquer un mot de passe exceptionnel. Ce n’est pas un moyen pratique de mesurer la force du mot de passe.

Aborder le problème dans l'autre sens est beaucoup plus facile à gérer: si nous fournissons un ensemble d'exigences de complexité, il est possible de juger de la force relative d'un mot de passe très rapidement. Évidemment, les exigences de complexité fournies doivent évoluer dans le temps, mais il y a beaucoup moins de variables à prendre en compte si nous abordons le problème de cette manière.

Une solution viable

Openwall propose un utilitaire autonome intitulé passwdqc (vraisemblablement , signifiant Vérificateur de qualité du mot de passe ). Le développeur Openwall, Solar Designer, semble être un expert de la cryptographie de bonne foi (ses travaux parlent d’eux-mêmes), et est donc qualifié pour créer un tel outil.

Pour mon cas d'utilisation particulier, il s'agit d'une solution beaucoup plus attrayante que celle d'utiliser un fragment de code JavaScript mal conçu vivant dans un coin sombre du Web.

L'établissement des paramètres pour vos besoins particuliers est la partie la plus difficile. La mise en œuvre est la partie facile.

Un exemple pratique

Je propose une implémentation simple en PHP pour permettre un démarrage rapide. Des avertissements standard s'appliquent.

Cet exemple suppose que nous fournissions une liste complète de mots de passe au script PHP. Il va sans dire que si vous utilisez des mots de passe réels (par exemple, ceux qui sont ignorés par un gestionnaire de mots de passe), vous devez être extrêmement prudent en ce qui concerne le traitement des mots de passe. Écrire simplement le dump de mot de passe non chiffré sur disque compromet la sécurité de vos mots de passe!

mots de passe.csv :

"Title","Password"
"My Test Password","password123"
"Your Test Password","123456!!!"
"A strong password","NFYbCoHC5S7dngitqCD53tvQkAu3dais"

password-check.php :

<?php

//A few handy examples from other users:
//http://php.net/manual/en/function.str-getcsv.php#117692

$csv = array_map('str_getcsv', file('passwords.csv'), [',']);

array_walk($csv, function(&$a) use ($csv) {
    $a = array_combine($csv[0], $a);
});

//Remove column header.

array_shift($csv);

//Define report column headers.

$results[] = [
    'Title',
    'Result',
    'Exit Code',
];

$i = 1;

foreach ($csv as $p) {
    $row['title'] = $p['Title'];

    //If the value contains a space, it's considered a passphrase.

    $isPassphrase = stristr($p['Password'], ' ') !== false ? true : false;

    $cmd = 'echo ' . escapeshellarg($p['Password']) . ' | pwqcheck -1 min=32,24,22,20,16 max=128';

    if ($isPassphrase) {
        $cmd .= ' passphrase=3';
    }
    else {
        $cmd .= ' passphrase=0';
    }

    $output = null;
    $exitCode = null;

    $stdOut = exec($cmd, $output, $exitCode);

    //Exit code 0 represents an unacceptable password (not an error).
    //Exit code 1 represents an acceptable password (it meets the criteria).

    if ($exitCode === 0 || $exitCode === 1) {
        $row['result'] = trim($stdOut);
        $row['exitCode'] = $exitCode;
    }
    else {
        $row['result'] = 'An error occurred while calling pwqcheck';
        $row['exitCode'] = null;
    }

    $results[$i] = $row;

    $i++;
}

$reportFile = 'report.csv';

$fp = @fopen($reportFile, 'w');

if ($fp !== false) {
    foreach ($results as $p) {
        fputcsv($fp, $p);
    }

    fclose($fp);
}
else {
    die($reportFile . ' could not be opened for writing (destination is not writable or file is in use)');
}

exit;

Résultat report.csv :

Title,Result,"Exit Code"
"My Test Password","Bad passphrase (too short)",1
"Your Test Password","Bad passphrase (too short)",1
"A strong password",OK,0

Conclusion

Je n'ai pas encore trouvé de solution plus complète sur le Web. Inutile de dire que toutes les autres recommandations sont les bienvenues.

Évidemment, cette approche n’est pas idéale pour certains cas d’utilisation (par exemple, un "indicateur de force de mot de passe" implémenté "côté client"). Même dans ce cas, il serait trivial de passer un appel AJAX à une ressource côté serveur qui renvoie une réponse succès / échec en utilisant l’approche décrite ci-dessus, mais une telle approche présumait le potentiel d’abus (par exemple, attaques par déni de service) et nécessiterait: communication sécurisée entre le client et le serveur, ainsi que l’acceptation des risques liés à la transmission du mot de passe non haché.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top