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
En fonction de la langue, j'utilise généralement des expressions régulières pour vérifier si elle contient:
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:
Vous pouvez ajuster ce qui précède pour répondre à vos besoins.
L'approche orientée objet serait un ensemble de règles. Attribuez un poids à chaque règle et parcourez-la. En pseudo-code:
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 mesures les plus simples à vérifier sont:
Cracklib est génial, et dans les paquetages les plus récents, 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. Il ne nécessite-t-il 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 la fonction libcrypt "FascistGecos" mais sans exiger que l’utilisateur existe dans/etc/passwd.
Ma bibliothèque/ ctypescracklib est disponible sur github
Quelques exemples d'utilisations:
>>> 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:
-1 identique à l'identifiant
+ 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 minuscules et de majuscules
+ 1 contient de la 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' vaut en vaut la peine.
'abc123ABC! @ £' = score 6/6 sinon un sur-ensemble de nom d'utilisateur
peut-être que cela devrait marquer moins.
Il existe un outil gratuit et ouvert John the Ripper password cracker, qui est un excellent moyen de vérifier une base de données de mots de passe existante.
Eh bien c'est 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;
}
J'ai écrit une petite application Javascript. Jetez un oeil: Pourtant, un autre compteur de mot de passe . Vous pouvez télécharger la source et l’utiliser/la modifier sous GPL. S'amuser!
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 classe de règles 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: Non testé par l'unité
En plus de l'approche standard consistant à mélanger des caractères alphanumériques, numériques et des symboles, j'ai remarqué que, lorsque je me suis inscrit à MyOpenId la semaine dernière, le vérificateur de mot de passe vous indique si votre mot de passe est basé sur un dictionnaire, même si vous ajoutez des numéros ou remplacez des alphas par des numéros similaires. (en utilisant zéro au lieu de 'o', '1' au lieu de 'i', etc.).
J'ai été très impressionné.