web-dev-qa-db-fra.com

Quel est le meilleur moyen de vérifier la force d'un mot de passe?

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
42
Ed L

En fonction de la langue, j'utilise généralement des expressions régulières pour vérifier si elle contient:

  • Au moins une lettre 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 ajuster ce qui précède pour répondre à vos besoins.

18
VirtuosiMedia

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;

}
9
user9116

Les deux mesures les plus simples à vérifier sont:

  1. Longueur. Je dirais au moins 8 caractères.
  2. 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.
3
Dave Webb

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'
2
Sean Reifschneider

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.

2
siznax

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.

1
tante

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;
} 
1
varun

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!

0
ReneS

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é

0
SapphireSun

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é.

0
Steve Morgan