Quel est le meilleur moyen de vérifier la force d'un mot de passe?
Question
Quel est le meilleur moyen de s'assurer qu'un mot de passe fourni par l'utilisateur est un mot de passe fort dans un formulaire d'inscription ou de changement de mot de passe?
Une idée que j'avais (en python)
def validate_password(passwd):
conditions_met = 0
conditions_total = 3
if len(passwd) >= 6:
if passwd.lower() != passwd: conditions_met += 1
if len([x for x in passwd if x.isdigit()]) > 0: conditions_met += 1
if len([x for x in passwd if not x.isalnum()]) > 0: conditions_met += 1
result = False
print conditions_met
if conditions_met >= 2: result = True
return result
La solution
1: Éliminez les mots de passe fréquemment utilisés
Vérifiez les mots de passe entrés par rapport à une liste de mots de passe souvent utilisés (voir par exemple les 100 000 premiers mots de passe figurant dans la liste de mots de passe LinkedIn divulgués: http://www.adeptus-mechanicus.com/codex/linkhap/combo_not.zip ), veillez à inclure substitutions de leetspeek :
A @, E3, B8, S5, etc.
Supprimez les parties du mot de passe qui ont heurté cette liste de la phrase entrée, avant de passer à la partie 2 ci-dessous.
2: ne forcez aucune règle sur l'utilisateur
La règle d'or des mots de passe est que plus c'est long, mieux c'est.
Oubliez l'utilisation forcée des majuscules, des chiffres et des symboles car (la grande majorité des) utilisateurs:
- Faites de la première lettre une capitale;
- Mettez le numéro 1
à la fin;
- Placez un !
après si un symbole est requis.
Vérifiez plutôt la force du mot de passe
Pour un bon point de départ, voir: http://www.passwordmeter.com/
Je suggère au minimum les règles suivantes:
Additions (better passwords)
-----------------------------
- Number of Characters Flat +(n*4)
- Uppercase Letters Cond/Incr +((len-n)*2)
- Lowercase Letters Cond/Incr +((len-n)*2)
- Numbers Cond +(n*4)
- Symbols Flat +(n*6)
- Middle Numbers or Symbols Flat +(n*2)
- Shannon Entropy Complex *EntropyScore
Deductions (worse passwords)
-----------------------------
- Letters Only Flat -n
- Numbers Only Flat -(n*16)
- Repeat Chars (Case Insensitive) Complex -
- Consecutive Uppercase Letters Flat -(n*2)
- Consecutive Lowercase Letters Flat -(n*2)
- Consecutive Numbers Flat -(n*2)
- Sequential Letters (3+) Flat -(n*3)
- Sequential Numbers (3+) Flat -(n*3)
- Sequential Symbols (3+) Flat -(n*3)
- Repeated words Complex -
- Only 1st char is uppercase Flat -n
- Last (non symbol) char is number Flat -n
- Only last char is symbol Flat -n
Suivre passwordmeter n'est pas suffisant, car son algorithme naïf est visible Password1! aussi bon, alors qu'il est exceptionnellement faible. Assurez-vous de ne pas tenir compte des majuscules lors de la notation, ainsi que des chiffres et des symboles de fin (conformément aux 3 dernières règles).
Calcul de l'entropie de Shannon
Voir: le moyen le plus rapide de calculer l'entropie en Python
3: n'autorisez aucun mot de passe trop faible
.
Plutôt que d'obliger l'utilisateur à se plier à des règles auto-destructrices, autorisez tout ce qui donnera un score suffisant. La hauteur dépend de votre cas d'utilisation.
Et surtout
Lorsque vous acceptez le mot de passe et le stockez dans une base de données, assurez-vous de le saler et de le hacher! .
Autres conseils
En fonction de la langue, j'utilise généralement des expressions régulières pour vérifier si elle contient:
- Au moins une majuscule et une lettre minuscule
- Au moins un numéro
- Au moins un caractère spécial
- une longueur d'au moins six caractères
Vous pouvez exiger tout ce qui précède ou utiliser un type de script de mesure de la force. Pour mon mesureur de force, si le mot de passe a la bonne longueur, il est évalué comme suit:
- Une condition remplie: mot de passe faible
- Deux conditions remplies: mot de passe moyen
- Toutes les conditions sont remplies: mot de passe fort
Vous pouvez adapter ce qui précède à vos besoins.
L’approche orientée objet serait un ensemble de règles. Attribuez un poids à chaque règle et parcourez-la. En code pseudo:
abstract class Rule {
float weight;
float calculateScore( string password );
}
Calcul du score total:
float getPasswordStrength( string password ) {
float totalWeight = 0.0f;
float totalScore = 0.0f;
foreach ( rule in rules ) {
totalWeight += weight;
totalScore += rule.calculateScore( password ) * rule.weight;
}
return (totalScore / totalWeight) / rules.count;
}
Un exemple d'algorithme de règle, basé sur le nombre de classes de caractères présentes:
float calculateScore( string password ) {
float score = 0.0f;
// NUMBER_CLASS is a constant char array { '0', '1', '2', ... }
if ( password.contains( NUMBER_CLASS ) )
score += 1.0f;
if ( password.contains( UPPERCASE_CLASS ) )
score += 1.0f;
if ( password.contains( LOWERCASE_CLASS ) )
score += 1.0f;
// Sub rule as private method
if ( containsPunctuation( password ) )
score += 1.0f;
return score / 4.0f;
}
Les deux métriques les plus simples à vérifier sont:
- Longueur. Je dirais au moins 8 caractères.
- Nombre de classes de caractères différentes contenues dans le mot de passe. Il s’agit généralement de lettres minuscules, de lettres majuscules, de chiffres, de signes de ponctuation et autres. Un mot de passe fort contiendra des caractères d'au moins trois de ces classes; si vous forcez un nombre ou un autre caractère non alphabétique, vous réduisez considérablement l'efficacité des attaques par dictionnaire.
Cracklib est génial, et dans les nouveaux packages, un module Python est disponible. Cependant, sur les systèmes qui ne l’ont pas encore, comme CentOS 5, j’ai écrit un wrapper ctypes pour la clé de chiffrement système. Cela fonctionnerait également sur un système sur lequel vous ne pouvez pas installer python-libcrypt. nécessite python avec les types de types disponibles, vous devez donc installer et utiliser le paquet python26.
Il présente également l’avantage de pouvoir prendre le nom d’utilisateur et de rechercher les mots de passe qui le contiennent ou qui sont sensiblement similaires, comme le libcrypt " FascistGecos " fonction mais sans exiger que l'utilisateur existe dans / etc / passwd.
Ma bibliothèque de ctypescracklib est disponible sur github
Quelques exemples d'utilisation:
>>> FascistCheck('jafo1234', 'jafo')
'it is based on your username'
>>> FascistCheck('myofaj123', 'jafo')
'it is based on your username'
>>> FascistCheck('jxayfoxo', 'jafo')
'it is too similar to your username'
>>> FascistCheck('cretse')
'it is based on a dictionary word'
après avoir lu les autres réponses utiles, voici ce que je vais faire avec:
-1 identique au nom d'utilisateur
+0 contient le nom d'utilisateur
+1 plus de 7 caractères
+1 plus de 11 caractères
+1 contient des chiffres
+1 mélange de majuscules et de minuscules
+1 contient une ponctuation
+1 caractère non imprimable
pwscore.py:
import re
import string
max_score = 6
def score(username,passwd):
if passwd == username:
return -1
if username in passwd:
return 0
score = 0
if len(passwd) > 7:
score+=1
if len(passwd) > 11:
score+=1
if re.search('\d+',passwd):
score+=1
if re.search('[a-z]',passwd) and re.search('[A-Z]',passwd):
score+=1
if len([x for x in passwd if x in string.punctuation]) > 0:
score+=1
if len([x for x in passwd if x not in string.printable]) > 0:
score+=1
return score
exemple d'utilisation:
import pwscore
score = pwscore(username,passwd)
if score < 3:
return "weak password (score="
+ str(score) + "/"
+ str(pwscore.max_score)
+ "), try again."
probablement pas le plus efficace, mais semble raisonnable. pas sûr que FascistCheck = > 'trop similaire à nom d'utilisateur' est ça vaut le coup.
'abc123ABC! @ £' = score 6/6 si ce n'est pas un sur-ensemble de nom d'utilisateur
peut-être que cela devrait marquer moins.
Il existe un pirate de mots de passe John the Ripper ouvert et gratuit, qui est un excellent moyen de vérifier base de données de mots de passe existante.
Eh bien, voici ce que j'utilise:
var getStrength = function (passwd) {
intScore = 0;
intScore = (intScore + passwd.length);
if (passwd.match(/[a-z]/)) {
intScore = (intScore + 1);
}
if (passwd.match(/[A-Z]/)) {
intScore = (intScore + 5);
}
if (passwd.match(/\d+/)) {
intScore = (intScore + 5);
}
if (passwd.match(/(\d.*\d)/)) {
intScore = (intScore + 5);
}
if (passwd.match(/[!,@#$%^&*?_~]/)) {
intScore = (intScore + 5);
}
if (passwd.match(/([!,@#$%^&*?_~].*[!,@#$%^&*?_~])/)) {
intScore = (intScore + 5);
}
if (passwd.match(/[a-z]/) && passwd.match(/[A-Z]/)) {
intScore = (intScore + 2);
}
if (passwd.match(/\d/) && passwd.match(/\D/)) {
intScore = (intScore + 2);
}
if (passwd.match(/[a-z]/) && passwd.match(/[A-Z]/) && passwd.match(/\d/) && passwd.match(/[!,@#$%^&*?_~]/)) {
intScore = (intScore + 2);
}
return intScore;
}
En plus de l’approche standard consistant à mélanger des caractères alphanumériques, numériques et des symboles, j’ai remarqué lors de mon inscription à MyOpenId la semaine dernière que le vérificateur de mot de passe vous indique si votre mot de passe est basé sur un mot du dictionnaire, même si vous ajoutez des chiffres ou remplacez des alphas. avec des chiffres similaires (utilisez zéro au lieu de 'o', '1' au lieu de 'i', etc.).
J'ai été très impressionné.
J'ai écrit une petite application Javascript. Jetez un coup d'œil à: Encore un autre indicateur de mot de passe . Vous pouvez télécharger la source et l’utiliser / la modifier sous GPL. Amusez-vous!
Je ne sais pas si quelqu'un trouvera cela utile, mais j'ai vraiment aimé l'idée d'un jeu de règles comme suggéré par phear. Je suis donc allé écrire une règle en classe Python 2.6 (bien que ce soit probablement compatible avec la version 2.5):
import re
class SecurityException(Exception):
pass
class Rule:
"""Creates a rule to evaluate against a string.
Rules can be regex patterns or a boolean returning function.
Whether a rule is inclusive or exclusive is decided by the sign
of the weight. Positive weights are inclusive, negative weights are
exclusive.
Call score() to return either 0 or the weight if the rule
is fufilled.
Raises a SecurityException if a required rule is violated.
"""
def __init__(self,rule,weight=1,required=False,name=u"The Unnamed Rule"):
try:
getattr(rule,"__call__")
except AttributeError:
self.rule = re.compile(rule) # If a regex, compile
else:
self.rule = rule # Otherwise it's a function and it should be scored using it
if weight == 0:
return ValueError(u"Weights can not be 0")
self.weight = weight
self.required = required
self.name = name
def exclusive(self):
return self.weight < 0
def inclusive(self):
return self.weight >= 0
exclusive = property(exclusive)
inclusive = property(inclusive)
def _score_regex(self,password):
match = self.rule.search(password)
if match is None:
if self.exclusive: # didn't match an exclusive rule
return self.weight
elif self.inclusive and self.required: # didn't match on a required inclusive rule
raise SecurityException(u"Violation of Rule: %s by input \"%s\"" % (self.name.title(), password))
elif self.inclusive and not self.required:
return 0
else:
if self.inclusive:
return self.weight
elif self.exclusive and self.required:
raise SecurityException(u"Violation of Rule: %s by input \"%s\"" % (self.name,password))
elif self.exclusive and not self.required:
return 0
return 0
def score(self,password):
try:
getattr(self.rule,"__call__")
except AttributeError:
return self._score_regex(password)
else:
return self.rule(password) * self.weight
def __unicode__(self):
return u"%s (%i)" % (self.name.title(), self.weight)
def __str__(self):
return self.__unicode__()
J'espère que quelqu'un trouvera cela utile!
Exemple d'utilisation:
rules = [ Rule("^foobar",weight=20,required=True,name=u"The Fubared Rule"), ]
try:
score = 0
for rule in rules:
score += rule.score()
except SecurityException e:
print e
else:
print score
AVERTISSEMENT: pas testé sur l'unité
Si vous avez le temps, lancez un cracker de mots de passe.
Les vérificateurs de la force du mot de passe, et si vous avez le temps + les ressources (sa justification s’applique uniquement si vous vérifiez plusieurs mots de passe), utilisez Rainbow Tables.
Avec une série de vérifications pour s'assurer qu'il répond aux critères minimaux:
- au moins 8 caractères
- contient au moins un symbole non alphanumérique
- ne correspond pas ou ne contient pas nom d'utilisateur / email / etc.
- etc
Voici un plugin jQuery qui indique la force du mot de passe (non essayé moi-même): http://phiras.wordpress.com / 2007/04/08 / plug-in-force-mètre-à-jquery /
Et la même chose a été portée en PHP: http://www.alixaxel.com/wordpress / 2007/06/09 / algorithme-mot-de-passe-php /
Quel est le meilleur moyen de s'assurer qu'un mot de passe fourni par l'utilisateur est un mot de passe fort dans un formulaire d'inscription ou de changement de mot de passe?
N'évaluez pas la complexité et / ou la force, les utilisateurs trouveront un moyen de duper votre système ou seront tellement frustrés qu'ils partiront. Cela ne vous conduira que dans des situations comme celle-ci . Exige juste une certaine longueur et que les mots de passe divulgués ne soient pas utilisés. Points bonus: assurez-vous que ce que vous mettez en place autorise l'utilisation de gestionnaires de mots de passe et / ou de 2FA.