Regex para validar a força da senha

142

Meus critérios de força da senha são os seguintes:

  • Comprimento de 8 caracteres
  • 2 letras em maiúsculas
  • 1 Caráter Especial (!@#$&*)
  • 2 numerais (0-9)
  • 3 letras em minúsculas

Alguém pode me dar regex para o mesmo. Todas as condições devem ser atendidas por senha.

Ajay Kelkar
fonte
2
Você está realmente disposto a confiar suas medidas de segurança de senha na Internet em geral?
Borealid 28/02
12
@Borealid: publicar suas políticas de senha geralmente não deve afetar significativamente sua segurança. Se isso acontecer, suas políticas serão ruins ("Somente passworde hello123são senhas válidas!").
Joachim Sauer
3
@Joachim Sauer: Não foi isso que eu quis dizer. O que eu quis dizer foi que o pôster provavelmente confiará em qualquer expressão regular que ele receber. Não é uma boa ideia.
Borealid 28/02
3
Na verdade, esta regex vai ser no código de serviço, eu vou estar testando para casos diff não confiar cegamente que :)
Ajay Kelkar
9
Regras de senha complexas geralmente não levam a senhas mais seguras; importante é apenas um tamanho mínimo. As pessoas não conseguem se lembrar de toneladas de senhas fortes e essas regras podem interferir em bons esquemas de senhas. As pessoas podem ser muito inventivas para ignorar essas regras, por exemplo, usando senhas fracas como "Senha-2014". Muitas vezes, você acaba com senhas mais fracas do que com senhas mais fortes.
martinstoeckli

Respostas:

428

Você pode fazer essas verificações usando declarações positivas de antecipação:

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

Link Rubular

Explicação:

^                         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.
codaddict
fonte
92
Para quem quer um comprimento de, pelo menos n, substituir .{8}com.{n,}
NullUserException
14
+1 para uma explicação completa. Minhas regras de senha são diferentes, mas com base na sua resposta, posso adaptar o regex.
Morvael
14
Obrigado por descrever o que está acontecendo na regex. Isso serve como um excelente exemplo de aprendizado para aqueles que nunca realmente se deram bem com a sintaxe.
4
Também aprecio a explicação do regex. Muitas vezes eu uso regex complexo que encontrei, sem realmente entender o que está acontecendo.
Nicholas Smith
4
Ótimo padrão, gostaria de saber por que não usar quantificadores? Pelo menos 1 especial, 1 número, 1 caractere especial, 8 caracteres: ^ (? =. * ([AZ]) {1,}) (? =. * [! @ # $ & *] {1,}) ( ? =. * [0-9] {1,}) (? =. * [Az] {1,}). {8.100} $
RockOnGom
11

Você pode usar antecipações positivas de comprimento zero para especificar cada uma de suas restrições separadamente:

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

Se o mecanismo regex não suportar a \pnotação e o ASCII puro for suficiente, você poderá substituir \p{Lu}por [A-Z]e \p{Ll}com [a-z].

Joachim Sauer
fonte
8

As respostas dadas acima são perfeitas, mas sugiro usar várias expressões regulares menores, em vez de grandes.
A divisão do regex longo tem algumas vantagens:

  • facilidade para escrever e ler
  • facilidade para depurar
  • facilidade para adicionar / remover parte do regex

Geralmente, essa abordagem mantém o código de fácil manutenção .

Dito isto, compartilho um pedaço de código que escrevo no Swift como exemplo:

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
fonte
Além disso, ao usar regex complexo como acima, é muito fácil se abrir para um retorno catastrófico ( regular-expressions.info/catastrophic.html ). Isso pode passar despercebido até que um dia seu servidor seja interrompido com 100% da CPU porque um usuário usou uma senha "estranha". Exemplo: ^ ([a-Z0-9] +) {8,} $ (você pode ver o erro?)
Akzent
5

Eu sugeriria adicionar

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

A solução do codaddict funciona bem, mas essa é um pouco mais eficiente: (sintaxe do 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)

As classes de caracteres negados consomem tudo até o caractere desejado em uma única etapa, exigindo zero retorno. (A solução dot star funciona muito bem, mas requer algum retorno.) Obviamente, com sequências curtas de destino, como senhas, essa melhoria de eficiência será insignificante.

ridgerunner
fonte
Você poderia verificar se está correto? Estou em dúvida por causa da abertura do suporte redondo na primeira linha entre aspas duplas triplas e ponto de interrogação. Eu posso ver que o comentário do Python (hash) é posterior. Não consigo ver o suporte redondo de fechamento correspondente próximo à âncora final (cifrão). Devo mencionar que eu não sou um regex profy.
Lospejos 2/11
@lospejos - O # não é o início de um comentário regular de uma linha. Esse hash faz parte de um grupo de comentários que começa com a (?#e termina com a ). Não há parênteses desequilibrados neste regex.
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))
ivy qin
fonte
1

A solução do @ codaddict funcionará.

Você também deve alterar algumas de suas regras para:

  1. Adicione mais caracteres especiais, como%, ^, (,), -, _, + e ponto final. Estou adicionando todos os caracteres especiais que você perdeu acima dos sinais numéricos nos teclados dos EUA. Fuja dos que o regex usa.
  2. Crie a senha com 8 ou mais caracteres. Não é apenas um número estático 8.

Com as melhorias acima, e para mais flexibilidade e legibilidade, eu modificaria o regex para.

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

Explicação 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

Explicação detalhada

^                             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

E, finalmente, para fins de teste, aqui está um robulink com a regex acima

lsu_guy
fonte
Obrigado @AFract. Estou usando no meu código. Eu gosto de legibilidade e capacidade de repetição, pois quando você precisar voltar e alterá-lo no futuro, ou seja, no caso de uma alteração na política de senha :)
lsu_guy 25/02
0

Para PHP, isso funciona bem!

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

neste caso, o resultado é verdadeiro

Thsks for @ridgerunner

Alejandro Peña
fonte
por que não return preg_match("/^(?=(?:[^A-Z]*[A-Z]){2})(?=(?:[^0-9]*[0-9]){2}).{8,}$/", 'CaSu4Li8')?
aloisdg movendo para codidact.com 22/11
0

Outra solução:

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

A senha deve atender a pelo menos três das quatro regras de complexidade a seguir,

[pelo menos 1 caractere em maiúscula (AZ) pelo menos 1 caractere em minúscula (az) pelo menos 1 dígito (0-9) pelo menos 1 caractere especial - não se esqueça de tratar o espaço como caracteres especiais também]

pelo menos 10 caracteres

no máximo 128 caracteres

não mais que 2 caracteres idênticos seguidos (por exemplo, 111 não 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
fonte
0

Infelizmente, toda a regex acima não funcionou para mim. As regras básicas de uma senha forte são

  • Deve conter pelo menos uma letra maiúscula
  • Deve conter pelo menos uma letra minúscula
  • Deve conter pelo menos um número
  • Deve conter pelo menos um caractere especial
  • E comprimento mínimo

Então, o Best Regex seria

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

O regex acima tem um comprimento mínimo de 8. Você pode alterá-lo de {8,} para { any_number ,}

Modificação nas regras?

digamos que você queira um mínimo de x caracteres minúsculos, y caracteres maiúsculos, z caracteres, número total mínimo de comprimento w . Em seguida, tente abaixo da regex

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

Nota: Altere x , y , z , w na expressão regular

Editar: resposta regex atualizada

Edit2: modificação adicionada

Juned Khatri
fonte
Seu regex está correspondendo. 12345678Tem certeza de que é uma senha forte ? Por favor, tente sua regex antes de postar.
Toto
É melhor, mas não responde à pergunta, eles querem 1) 8 caracteres. 2) 2 letras em maiúsculas. 3) 1 caractere especial (! @ # $ & *). 4) 2 numerais (0-9). 5) 3 letras em minúsculas.
Toto
@Toto Você pode compartilhar seus pensamentos agora?
Juned Khatri
Sua regex não leva em consideração que as duas letras maiúsculas obrigatórias podem ser separadas por outros caracteres, a mesma observação para letras minúsculas e dígitos. A resposta válida é a que foi aceita.
Toto