Pregunta

¿Cuál es la mejor manera de garantizar que la 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
¿Fue útil?

Solución

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

2:No fuerces ninguna regla al usuario.

La regla de oro de las contraseñas es que más tiempo, mejor.
Olvídese del uso forzado de mayúsculas, números y símbolos porque (la gran mayoría de) los usuarios:- Poner 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 su lugar, 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

Solo siguiendo medidor de contraseñas no es suficiente, porque efectivamente su ingenuo algoritmo ve Contraseña1! bueno, mientras que es excepcionalmente débil.Asegúrate de ignorar las letras mayúsculas iniciales al puntuar, así como los números y símbolos finales (según las últimas 3 reglas).

Calculando 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 someterse a reglas contraproducentes, permita cualquier cosa que le dé una puntuación suficientemente alta.La altura depende de su caso de uso.

Y más importante
Cuando acepta la contraseña y la almacena en una base de datos, ¡Asegúrate de salarlo y triturarlo!.

Otros consejos

Dependiendo del idioma, suelo utilizar expresiones regulares para comprobar si tiene:

  • Al menos un mayúscula y una letra minúscula
  • Al menos un numero
  • Al menos un carácter especial
  • Una longitud de al menos seis caracteres.

Puede solicitar todo lo anterior o utilizar un script tipo 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 debil
  • Se cumplieron dos condiciones:contraseña mediana
  • Todas las condiciones cumplidas:contraseña segura

Puede ajustar lo anterior para satisfacer sus necesidades.

El enfoque orientado a objetos sería un conjunto de reglas.Asigne un peso a cada regla y repítalas.En pseudocódigo:

abstract class Rule {

    float weight;

    float calculateScore( string password );

}

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

}

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

  1. Longitud.Yo diría 8 caracteres como mínimo.
  2. Número de clases de caracteres diferentes que contiene la contraseña.Suelen ser letras minúsculas, letras mayúsculas, números, 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, reducirá significativamente la efectividad de los ataques de diccionario.

Cracklib es excelente y en los paquetes más nuevos hay un módulo de Python disponible para ello.Sin embargo, en sistemas que aún no lo tienen, como CentOS 5, escribí un contenedor ctypes para el sistema cryptlib.Esto también funcionaría en un sistema en el que no se puede instalar python-libcrypt.Él hace 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 que sean sustancialmente similares, como la función libcrypt "FascistGecos" pero sin requerir que el usuario exista en /etc/passwd.

Mi La biblioteca ctypescracklib está disponible en github

Algunos usos de ejemplo:

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

Después de leer las otras respuestas útiles, esto es lo que voy a hacer:

-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 combinación de minúsculas y mayúsculas
+1 contiene puntuación
+1 carácter 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

uso de ejemplo:

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 fascistcheck => 'demasiado similar al nombre de usuario' vale la pena.

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

tal vez eso debería tener una puntuación más baja.

Ahí está el abierto y libre Juan el Destripador Cracker de contraseñas, que es una excelente manera de verificar una base de datos de contraseñas existente.

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

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

Me quedé bastante impresionado.

Escribí una pequeña aplicación Javascript.Echar un vistazo: Otro medidor de contraseñas más.Puede descargar la fuente y usarla/modificarla bajo GPL.¡Divertirse!

No sé si a alguien le resultará útil, pero realmente me gustó la idea de un conjunto de reglas como lo sugirió Phear, así que escribí una clase de reglas de Python 2.6 (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!

Uso de ejemplo:

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:Unidad no probada

Si tienes tiempo, ejecuta un descifrador de contraseñas.

Comprobadores de seguridad de contraseñas, y si tiene tiempo y recursos (está justificado solo si está verificando más de unas pocas contraseñas), use Rainbow Tables.

Con una serie de comprobaciones para garantizar que cumple con unos criterios mínimos:

  • Al menos 8 caracteres de longitud
  • contiene al menos un símbolo no alfanumérico
  • no coincide ni contiene nombre de usuario/correo electrónico/etc.
  • etc.

Aquí hay un complemento jQuery que informa la seguridad de la contraseña (no lo probé yo mismo):http://phiras.wordpress.com/2007/04/08/password-strength-meter-a-jquery-plugin/

Y lo mismo portado a PHP:http://www.alixaxel.com/wordpress/2007/06/09/php-password-strength-algorithm/

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

No evalúe la complejidad o la fortaleza, los usuarios encontrarán una manera de engañar a su sistema o se frustrarán tanto que lo abandonarán.Eso solo te traerá situaciones como esto.Solo requiere cierta longitud y que no se utilicen contraseñas filtradas.Puntos extra:asegúrese de que todo lo que implemente permita el uso de administradores de contraseñas y/o 2FA.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top