Regex para validar la seguridad de la contraseña

142

El criterio de seguridad de mi contraseña es el siguiente:

  • 8 caracteres de longitud
  • 2 letras en mayúscula
  • 1 personaje especial (!@#$&*)
  • 2 números (0-9)
  • 3 letras en minúscula

¿Alguien puede darme expresiones regulares por igual? Todas las condiciones deben cumplirse por contraseña.

Ajay Kelkar
fuente
2
¿Está realmente dispuesto a confiar sus medidas de seguridad de contraseña en Internet en general?
Borealid
12
@Borealid: la publicación de sus políticas de contraseña generalmente no debería afectar significativamente su seguridad. Si lo hace, entonces sus políticas son malas ("¡Solo passwordy hello123son contraseñas válidas!").
Joachim Sauer
3
@Joachim Sauer: Eso no es lo que quise decir. Lo que quise decir es que el póster probablemente solo va a confiar en cualquier expresión regular que reciba. No es una buena idea.
Borealid
3
En realidad, esta expresión regular estará en el código de servicio, probaré los casos de diferencias, no confío ciegamente en ella :)
Ajay Kelkar
9
Las reglas de contraseña complejas generalmente no conducirán a contraseñas más seguras, lo importante es solo una longitud mínima. La gente no puede recordar toneladas de contraseñas seguras, y tales reglas pueden interferir con buenos esquemas de contraseña. Las personas pueden ser muy ingeniosas para eludir tales reglas, por ejemplo, utilizando contraseñas débiles como "Password-2014". A menudo terminas con contraseñas más débiles en lugar de contraseñas más fuertes.
martinstoeckli

Respuestas:

428

Puede hacer estas comprobaciones utilizando aserciones positivas de anticipación:

^(?=.*[A-Z].*[A-Z])(?=.*[!@#$&*])(?=.*[0-9].*[0-9])(?=.*[a-z].*[a-z].*[a-z]).{8}$

Enlace rubular

Explicación:

^                         Start anchor
(?=.*[A-Z].*[A-Z])        Ensure string has two uppercase letters.
(?=.*[!@#$&*])            Ensure string has one special case letter.
(?=.*[0-9].*[0-9])        Ensure string has two digits.
(?=.*[a-z].*[a-z].*[a-z]) Ensure string has three lowercase letters.
.{8}                      Ensure string is of length 8.
$                         End anchor.
codictorio
fuente
92
Para cualquiera que quiera una duración de al menos n, reemplace .{8}con.{n,}
NullUserException
14
+1 para una explicación completa. Las reglas de mi contraseña son diferentes, pero en función de su respuesta, puedo adaptar la expresión regular.
Morvael
14
Gracias por describir lo que sucede en la expresión regular. Esto sirve como un gran ejemplo de aprendizaje para aquellos de nosotros que nunca hemos tenido realmente la sintaxis.
44
También aprecio la explicación de la expresión regular. Muchas veces uso expresiones regulares complejas que encontré, sin comprender realmente lo que está sucediendo.
Nicholas Smith
44
Gran patrón, me pregunto por qué no usar cuantificadores. Al menos 1 especial, 1 número, 1 carácter especial, 8 caracteres: ^ (? =. * ([AZ]) {1,}) (? =. * [! @ # $ & *] {1,}) ( ? =. * [0-9] {1,}) (? =. * [Az] {1,}). {8,100} $
RockOnGom
11

Puede usar look-aheads positivos de longitud cero para especificar cada una de sus restricciones por separado:

(?=.{8,})(?=.*\p{Lu}.*\p{Lu})(?=.*[!@#$&*])(?=.*[0-9])(?=.*\p{Ll}.*\p{Ll})

Si su motor regex no es compatible con la \pnotación y ASCII puro es suficiente, entonces puede reemplazar \p{Lu}con [A-Z]y \p{Ll}con [a-z].

Joachim Sauer
fuente
8

Las respuestas anteriores son perfectas, pero sugiero usar múltiples expresiones regulares más pequeñas en lugar de una grande.
Dividir la expresión regular larga tiene algunas ventajas:

  • facilidad para escribir y leer
  • facilidad para depurar
  • facilidad para agregar / eliminar parte de expresiones regulares

En general, este enfoque mantiene el código fácilmente mantenible .

Dicho esto, comparto un fragmento de código que escribo en Swift como ejemplo:

struct RegExp {

    /**
     Check password complexity

     - parameter password:         password to test
     - parameter length:           password min length
     - parameter patternsToEscape: patterns that password must not contains
     - parameter caseSensitivty:   specify if password must conforms case sensitivity or not
     - parameter numericDigits:    specify if password must conforms contains numeric digits or not

     - returns: boolean that describes if password is valid or not
     */
    static func checkPasswordComplexity(password password: String, length: Int, patternsToEscape: [String], caseSensitivty: Bool, numericDigits: Bool) -> Bool {
        if (password.length < length) {
            return false
        }
        if caseSensitivty {
            let hasUpperCase = RegExp.matchesForRegexInText("[A-Z]", text: password).count > 0
            if !hasUpperCase {
                return false
            }
            let hasLowerCase = RegExp.matchesForRegexInText("[a-z]", text: password).count > 0
            if !hasLowerCase {
                return false
            }
        }
        if numericDigits {
            let hasNumbers = RegExp.matchesForRegexInText("\\d", text: password).count > 0
            if !hasNumbers {
                return false
            }
        }
        if patternsToEscape.count > 0 {
            let passwordLowerCase = password.lowercaseString
            for pattern in patternsToEscape {
                let hasMatchesWithPattern = RegExp.matchesForRegexInText(pattern, text: passwordLowerCase).count > 0
                if hasMatchesWithPattern {
                    return false
                }
            }
        }
        return true
    }

    static func matchesForRegexInText(regex: String, text: String) -> [String] {
        do {
            let regex = try NSRegularExpression(pattern: regex, options: [])
            let nsString = text as NSString
            let results = regex.matchesInString(text,
                options: [], range: NSMakeRange(0, nsString.length))
            return results.map { nsString.substringWithRange($0.range)}
        } catch let error as NSError {
            print("invalid regex: \(error.localizedDescription)")
            return []
        }
    }
}
Luca Davanzo
fuente
Además, cuando se usa una expresión regular compleja como la anterior, es muy fácil abrirse a un retroceso catastrófico ( regular-expressions.info/catastrophic.html ). Esto puede pasar desapercibido hasta que un día su servidor se cuelgue con 100% de CPU porque un usuario usó una contraseña "extraña". Ejemplo: ^ ([a-z0-9] +) {8,} $ (¿puede ver el error?)
aKzenT
5

Sugeriría agregar

(?!.*pass|.*word|.*1234|.*qwer|.*asdf) exclude common passwords
Stuart
fuente
1

La solución de codaddict funciona bien, pero esta es un poco más eficiente: (sintaxis de Python)

password = re.compile(r"""(?#!py password Rev:20160831_2100)
    # Validate password: 2 upper, 1 special, 2 digit, 1 lower, 8 chars.
    ^                        # Anchor to start of string.
    (?=(?:[^A-Z]*[A-Z]){2})  # At least two uppercase.
    (?=[^!@#$&*]*[!@#$&*])   # At least one "special".
    (?=(?:[^0-9]*[0-9]){2})  # At least two digit.
    .{8,}                    # Password length is 8 or more.
    $                        # Anchor to end of string.
    """, re.VERBOSE)

Las clases de caracteres negados consumen todo hasta el carácter deseado en un solo paso, lo que requiere cero retroceso. (La solución dot star funciona bien, pero requiere un poco de retroceso). Por supuesto, con cadenas de destino cortas como contraseñas, esta mejora de eficiencia será insignificante.

ridgerunner
fuente
¿Podría verificar si es correcto? Tengo dudas debido a la apertura del corchete en primera línea entre triple comillas dobles y signo de interrogación. Puedo ver que el comentario de Python (hash) es posterior. No puedo ver al pariente que cierra el corchete cerca del anclaje final (signo de dólar). Debería mencionar que no soy un profgeo de expresiones regulares.
lospejos
@lospejos: el # no es el comienzo de un comentario normal de una línea. Este hash es parte de un grupo de comentarios que comienza con a (?#y termina con a ). No hay parens desequilibrados en esta expresión regular.
ridgerunner
1
import re

RegexLength=re.compile(r'^\S{8,}$')
RegexDigit=re.compile(r'\d')
RegexLower=re.compile(r'[a-z]')
RegexUpper=re.compile(r'[A-Z]')


def IsStrongPW(password):
    if RegexLength.search(password) == None or RegexDigit.search(password) == None or RegexUpper.search(password) == None or RegexLower.search(password) == None:
        return False
    else:
        return True

while True:
    userpw=input("please input your passord to check: \n")
    if userpw == "exit":
        break
    else:
        print(IsStrongPW(userpw))
hiedra qin
fuente
1

La solución de @ codaddict funcionará.

También debe considerar cambiar algunas de sus reglas para:

  1. Agregue más caracteres especiales, es decir,%, ^, (,), -, _, + y punto. Estoy agregando todos los caracteres especiales que te perdiste sobre los signos numéricos en los teclados de EE. UU. Escapa de los que usa la expresión regular.
  2. Haga la contraseña de 8 o más caracteres. No solo un número estático 8.

Con las mejoras anteriores, y para mayor flexibilidad y legibilidad, modificaría la expresión regular a.

^(?=.*[a-z]){3,}(?=.*[A-Z]){2,}(?=.*[0-9]){2,}(?=.*[!@#$%^&*()--__+.]){1,}.{8,}$

Explicación Básica

(?=.*RULE){MIN_OCCURANCES,}     Each rule block is shown by (){}. The rule and number of occurrences can then be easily specified and tested separately, before getting combined

Explicación detallada

^                             start anchor
(?=.*[a-z]){3,}               lowercase letters. {3,} indicates that you want 3 of this group
(?=.*[A-Z]){2,}               uppercase letters. {2,} indicates that you want 2 of this group
(?=.*[0-9]){2,}               numbers. {2,} indicates that you want 2 of this group
(?=.*[!@#$%^&*()--__+.]){1,}   all the special characters in the [] fields. The ones used by regex are escaped by using the \ or the character itself. {1,} is redundant, but good practice, in case you change that to more than 1 in the future. Also keeps all the groups consistent
{8,}                          indicates that you want 8 or more
$                             end anchor

Y, por último, para fines de prueba, aquí hay un enlace con la expresión regular anterior

lsu_guy
fuente
Gracias @AFract. Lo estoy usando en mi código. Me gusta la legibilidad y la capacidad de repetición, para cuando tenga que regresar y cambiarla en el futuro, es decir, en caso de un cambio en la política de contraseña :)
lsu_guy
0

Para PHP, ¡esto funciona bien!

 if(preg_match("/^(?=(?:[^A-Z]*[A-Z]){2})(?=(?:[^0-9]*[0-9]){2}).{8,}$/", 
 'CaSu4Li8')){
    return true;
 }else{
    return fasle;
 }

en este caso el resultado es verdadero

Gracias por @ridgerunner

Alejandro Peña
fuente
¿por qué no return preg_match("/^(?=(?:[^A-Z]*[A-Z]){2})(?=(?:[^0-9]*[0-9]){2}).{8,}$/", 'CaSu4Li8')?
aloisdg se muda a codidact.com el
0

Otra solución:

import re

passwordRegex = re.compile(r'''(
    ^(?=.*[A-Z].*[A-Z])                # at least two capital letters
    (?=.*[!@#$&*])                     # at least one of these special c-er
    (?=.*[0-9].*[0-9])                 # at least two numeric digits
    (?=.*[a-z].*[a-z].*[a-z])          # at least three lower case letters
    .{8,}                              # at least 8 total digits
    $
    )''', re.VERBOSE)

def userInputPasswordCheck():
    print('Enter a potential password:')
    while True:
        m = input()
        mo = passwordRegex.search(m) 
        if (not mo):
           print('''
Your password should have at least one special charachter,
two digits, two uppercase and three lowercase charachter. Length: 8+ ch-ers.

Enter another password:''')          
        else:
           print('Password is strong')
           return
userInputPasswordCheck()
S.Sergey
fuente
0

La contraseña debe cumplir al menos 3 de las siguientes 4 reglas de complejidad,

[al menos 1 carácter en mayúscula (AZ) al menos 1 carácter en minúscula (az) al menos 1 dígito (0-9) al menos 1 carácter especial - no olvide tratar el espacio como caracteres especiales también]

al menos 10 caracteres

a lo más 128 caracteres

no más de 2 caracteres idénticos seguidos (por ejemplo, 111 no está permitido)

'^ (?!. (.) \ 1 {2}) ((? =. [Az]) (? =. [AZ]) (? =. [0-9]) | (? =. [Az] ) (? =. [AZ]) (? =. [^ A-zA-Z0-9]) | (? =. [AZ]) (? =. [0-9]) (? =. [^ A -zA-Z0-9]) | (? =. [az]) (? =. [0-9]) (? =. * [^ a-zA-Z0-9])). {10,127} $ '

(?!. * (.) \ 1 {2})

(? =. [az]) (? =. [AZ]) (? =. * [0-9])

(? =. [az]) (? =. [AZ]) (? =. * [^ a-zA-Z0-9])

(? =. [AZ]) (? =. [0-9]) (? =. * [^ A-zA-Z0-9])

(? =. [az]) (? =. [0-9]) (? =. * [^ a-zA-Z0-9])

. {10.127}

Ra Vi
fuente
0

Desafortunadamente, todas las expresiones regulares anteriores no funcionaron para mí. Las reglas básicas de una contraseña segura son

  • Debe contener al menos una letra mayúscula
  • Debe contener al menos una letra minúscula
  • Debe contener al menos un número
  • Debe contener al menos un carácter especial
  • Y longitud mínima

Entonces, Best Regex sería

^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\$%\^&\*]).{8,}$

La expresión regular anterior tiene una longitud mínima de 8. Puede cambiarla de {8,} a { any_number ,}

Modificación en las reglas?

supongamos que desea un mínimo de x caracteres minúsculas, y caracteres mayúsculas, z caracteres números, longitud mínima total w . Entonces intente debajo de regex

^(?=.*[a-z]{x,})(?=.*[A-Z]{y,})(?=.*[0-9]{z,})(?=.*[!@#\$%\^&\*]).{w,}$

Nota: Cambiar x , y , z , w en regex

Editar: respuesta de expresiones regulares actualizada

Edit2: modificación añadida

Juned Khatri
fuente
Su expresión regular es coincidente 12345678¿está seguro de que es un fuerte contraseña? Por favor, intente su expresión regular antes de publicar.
Toto
Eso es mejor pero no responde la pregunta, quieren 1) 8 caracteres de longitud. 2) 2 letras en mayúscula. 3) 1 personaje especial (! @ # $ & *). 4) 2 números (0-9). 5) 3 letras en minúsculas.
Toto
@Toto ¿Puedes compartir tus pensamientos ahora?
Juned Khatri
Su expresión regular no tiene en cuenta que las 2 letras mayúsculas obligatorias podrían separarse con otros caracteres, el mismo comentario para minúsculas y dígitos. La respuesta válida es la que ha sido aceptada.
Toto el