web-development-kb-es.site

¿Cuál es la mejor manera de verificar la fortaleza de una contraseña?

¿Cuál es la mejor manera de garantizar que una contraseña proporcionada por el usuario sea una contraseña segura en un formulario de registro o cambio de contraseña?

Una idea que tuve (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
43
Ed L

1: Eliminar las contraseñas de uso frecuente
Verifique las contraseñas ingresadas en una lista de contraseñas de uso frecuente (consulte, por ejemplo, las 100.000 contraseñas principales en la lista de contraseñas filtradas de LinkedIn: http://www.adeptus-mechanicus.com/codex/linkhap/ combo_not.Zip ), asegúrese de incluir sustituciones leetspeek : A @, E3, B8, S5, etc.
Elimine las partes de la contraseña que golpearon contra esta lista de la frase ingresada, antes de pasar a la parte 2 a continuación.

2: No fuerce ninguna regla sobre el usuario

La regla de oro de las contraseñas es que más tiempo es mejor.
Olvídate del uso forzado de mayúsculas, números y símbolos porque (la gran mayoría de) los usuarios: - Convertirán la primera letra en mayúscula; - Pon el número 1 al final; - Poner un ! después de eso si se requiere un símbolo.

En cambio, verifique la seguridad de la contraseña

Para un punto de partida decente, consulte: http://www.passwordmeter.com/

Sugiero como mínimo las siguientes reglas:

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

Simplemente seguir passwordmeter no es suficiente, porque efectivamente su ingenuo algoritmo ve Password1! tan bueno, mientras que es excepcionalmente débil. Asegúrese de no tener en cuenta las letras mayúsculas iniciales al puntuar, así como los números y símbolos finales (según las últimas 3 reglas).

Cálculo de la entropía de Shannon
Ver: La forma más rápida de calcular la entropía en Python

3: No permita contraseñas que sean demasiado débiles
En lugar de obligar al usuario a doblegarse a las reglas autodestructivas, permita cualquier cosa que otorgue una puntuación lo suficientemente alta. Qué tan alto depende de su caso de uso.

Y lo más importante
Cuando aceptas la contraseña y la almacenas en una base de datos, ¡asegúrate de ponerle sal y hash! .

4
Johan

Dependiendo del idioma, generalmente uso expresiones regulares para verificar si tiene:

  • Al menos una letra mayúscula y una minúscula
  • Al menos un número
  • Al menos un personaje especial
  • Una longitud de al menos seis caracteres

Puede requerir todo lo anterior, o usar un tipo de script de medidor de fuerza. Para mi medidor de fuerza, si la contraseña tiene la longitud correcta, se evalúa de la siguiente manera:

  • Una condición cumplida: contraseña débil
  • Dos condiciones cumplidas: contraseña media
  • Se cumplen todas las condiciones: contraseña segura

Puede ajustar lo anterior para satisfacer sus necesidades.

17
VirtuosiMedia

El enfoque orientado a objetos sería un conjunto de reglas. Asigne un peso a cada regla e itere a través de ellas. En psuedo-code:

abstract class Rule {

    float weight;

    float calculateScore( string password );

}

Cálculo de la puntuación 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 algoritmo de regla de ejemplo, basado en el número de clases de caracteres presentes:

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;

}
10
user9116

Las dos métricas más simples para verificar son:

  1. Longitud. Yo diría que 8 caracteres como mínimo.
  2. Número de diferentes clases de caracteres que contiene la contraseña. Suelen ser letras minúsculas, letras mayúsculas, números y signos de puntuación y otros símbolos. Una contraseña segura contendrá caracteres de al menos tres de estas clases; Si fuerza un número u otro carácter no alfabético, reduce significativamente la efectividad de los ataques de diccionario.
3
Dave Webb

Cracklib es excelente, y en los paquetes más nuevos hay un módulo Python disponible para él. Sin embargo, en sistemas que aún no lo tienen, como CentOS 5, he escrito un contenedor de tipos para el sistema cryptlib. Esto también funcionaría en un sistema que no puede instalar python-libcrypt. requiere requiere python con ctypes disponibles, por lo que para CentOS 5 debe instalar y usar el paquete python26.

También tiene la ventaja de que puede tomar el nombre de usuario y buscar contraseñas que lo contengan o sean sustancialmente similares, como la función libcrypt "FascistGecos" pero sin requerir que el usuario exista en/etc/passwd.

My la biblioteca ctypescracklib está disponible en github

Algunos ejemplos de usos:

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

después de leer las otras respuestas útiles, esto es con lo que voy:

-1 igual que el nombre de usuario
+ 0 contiene nombre de usuario
+ 1 más de 7 caracteres
+ 1 más de 11 caracteres
+ 1 contiene dígitos
+ 1 mezcla de mayúsculas y minúsculas
+ 1 contiene puntuación
+ 1 char no imprimible

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

ejemplo de uso:

import pwscore
    score = pwscore(username,passwd)
    if score < 3:
        return "weak password (score=" 
             + str(score) + "/"
             + str(pwscore.max_score)
             + "), try again."

probablemente no sea el más eficiente, pero parece razonable. No estoy seguro de que FascistCheck => 'demasiado similar al nombre de usuario' valga la pena.

'abc123ABC! @ £' = puntaje 6/6 si no es un superconjunto de nombre de usuario

tal vez eso debería puntuar más bajo.

2
siznax

Existe el cracker de contraseña abierto y gratuito John the Ripper que es una excelente manera de verificar una base de datos de contraseñas existente.

1
tante

Bueno, esto es lo que uso:

   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

Escribí una pequeña aplicación Javascript. Eche un vistazo: Sin embargo, otro medidor de contraseña . Puede descargar la fuente y usarla/modificarla bajo GPL. ¡Que te diviertas!

0
ReneS

No sé si alguien encontrará esto útil, pero realmente me gustó la idea de un conjunto de reglas como lo sugiere phear, así que fui y escribí una regla Python 2.6 clase (aunque probablemente sea compatible con 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__()

¡Espero que alguien encuentre esto útil!

Ejemplo de uso:

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

DESCARGO DE RESPONSABILIDAD: No se ha probado la unidad

0
SapphireSun

Además del enfoque estándar de mezclar alfa, numérico y símbolos, noté que cuando me registré con MyOpenId la semana pasada, el verificador de contraseñas le dice si su contraseña se basa en una palabra del diccionario, incluso si agrega números o reemplaza alfa con números similares. (usando cero en lugar de 'o', '1' en lugar de 'i', etc.).

Estaba muy impresionado.

0
Steve Morgan