Regex per convalidare la sicurezza della password


142

I miei criteri di sicurezza della password sono i seguenti:

  • Lunghezza di 8 caratteri
  • 2 lettere maiuscole
  • 1 personaggio speciale (!@#$&*)
  • 2 numeri (0-9)
  • 3 lettere minuscole

Qualcuno può darmi per favore regex per lo stesso. Tutte le condizioni devono essere soddisfatte tramite password.


2
Sei davvero disposto a fidarti delle tue misure di sicurezza della password su Internet in generale?
Borealid,

12
@Borealid: la pubblicazione dei criteri per le password di solito non dovrebbe influire in modo significativo sulla sicurezza. In tal caso, le tue politiche sono errate ("Solo passworde hello123sono password valide!").
Joachim Sauer,

3
@Joachim Sauer: Non intendevo questo. Quello che intendevo dire era che il poster probabilmente si fiderà di qualunque regex riceva. Non è una buona idea.
Borealid,

3
In realtà questo regex sarà nel codice di servizio, testerò i casi diff, non fidarmi ciecamente di esso :)
Ajay Kelkar,

9
Le regole complesse per le password di solito non portano a password più sicure, importante è solo una lunghezza minima. Le persone non riescono a ricordare tonnellate di password complesse e tali regole possono interferire con buoni schemi di password. Le persone possono diventare molto inventive per aggirare tali regole, ad esempio utilizzando password deboli come "Password-2014". Spesso finisci con password più deboli anziché più forti.
martinstoeckli

Risposte:


428

Puoi fare questi controlli usando affermazioni positive:

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

Collegamento rubulare

Spiegazione:

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

92
Per chiunque desideri almeno una lunghezza n, sostituiscilo .{8}con.{n,}
NullUserException il

14
+1 per una spiegazione completa. Le mie regole di password sono diverse ma in base alla tua risposta posso adattare la regex.
Morvael,

14
Grazie per aver descritto cosa sta succedendo nella regex. Questo è un ottimo esempio di apprendimento per quelli di noi che non hanno mai avuto una vera sintassi.

4
Apprezzo anche la spiegazione della regex. Molte volte uso una regex complessa che ho trovato, senza capire veramente cosa sta succedendo.
Nicholas Smith,

4
Fantastico schema, mi chiedo perché non usare quantificatori? Almeno 1 speciale, 1 numero, 1 carattere speciale, 8 caratteri: ^ (? =. * ([AZ]) {1,}) (? =. * [! @ # $ & *] {1,}) ( ? =. * [0-9] {1,}) (? =. * [Az] {1,}). {8.100} $
RockOnGom

11

È possibile utilizzare look-looking positivi di lunghezza zero per specificare separatamente ciascuno dei vincoli:

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

Se il tuo motore regex non supporta la \pnotazione ed è sufficiente ASCII puro, puoi sostituirlo \p{Lu}con [A-Z]e \p{Ll}con [a-z].


8

Le risposte fornite sopra sono perfette, ma suggerisco di usare più regex più piccole piuttosto che grandi.
Dividere il regex lungo presenta alcuni vantaggi:

  • facilità di scrivere e leggere
  • facilità di debug
  • facilità per aggiungere / rimuovere parte di regex

Generalmente questo approccio mantiene il codice facilmente gestibile .

Detto questo, condivido un pezzo di codice che scrivo in Swift come esempio:

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 []
        }
    }
}

Inoltre, quando si utilizza regex complesso come sopra, è molto facile aprirsi al backtracking catastrofico ( regular-expressions.info/catastrophic.html ). Questo può passare inosservato fino a quando un giorno il tuo server si blocca con CPU al 100% perché un utente ha usato una password "strana". Esempio: ^ ([a-z0-9] +) {8,} $ (riesci a vedere l'errore?)
aKzenT

5

Suggerirei di aggiungere

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

1

La soluzione di codaddict funziona bene, ma questa è un po 'più efficiente: (Sintassi di 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)

Le classi di personaggi negate consumano tutto fino al personaggio desiderato in un unico passaggio, richiedendo zero backtracking. (La soluzione dot star funziona bene, ma richiede alcuni backtracking.) Naturalmente con stringhe target brevi come le password, questo miglioramento dell'efficienza sarà trascurabile.


Potresti verificare se è corretto? Sono in dubbio a causa dell'apertura della parentesi tonda nella prima riga tra la tripla doppia e il punto interrogativo. Vedo che il commento (hash) di Python è successivo. Non riesco a vedere la corrispondente parentesi tonda chiusa vicino all'ancoraggio finale (simbolo del dollaro). Dovrei dire che non sono un regex profy.
Lospejos,

@lospejos - Il # non è l'inizio di un normale commento di una riga. Questo hash fa parte di un gruppo di commenti che inizia con a (?#e termina con a ). Non ci sono parentesi sbilanciate in questa 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))

1

La soluzione di @ codaddict funzionerà.

Dovresti anche considerare di modificare alcune delle tue regole in:

  1. Aggiungi altri caratteri speciali, ad esempio%, ^, (,), -, _, + e punto. Sto aggiungendo tutti i caratteri speciali che hai perso sopra i segni numerici delle tastiere statunitensi. Sfuggire a quelli utilizzati da regex.
  2. Crea la password 8 o più caratteri. Non solo un numero statico 8.

Con i miglioramenti di cui sopra e per maggiore flessibilità e leggibilità, modificherei la regex in.

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

Spiegazione di base

(?=.*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

Spiegazione dettagliata

^                             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 infine, a scopo di test, ecco un robulink con la regex sopra


Grazie @AFract. Lo sto usando nel mio codice. Mi piace la leggibilità e la capacità di ripetizione, perché quando devi tornare indietro e cambiarlo in futuro, ad esempio in caso di modifica della politica della password :)
lsu_guy,

0

Per PHP, questo funziona benissimo!

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

in questo caso il risultato è vero

Grazie per @ridgerunner


perchè no return preg_match("/^(?=(?:[^A-Z]*[A-Z]){2})(?=(?:[^0-9]*[0-9]){2}).{8,}$/", 'CaSu4Li8')?
aloisdg si trasferisce su codidact.com il

0

Un'altra soluzione:

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

0

La password deve soddisfare almeno 3 delle seguenti 4 regole di complessità,

[almeno 1 carattere maiuscolo (AZ) almeno 1 carattere minuscolo (az) almeno 1 cifra (0-9) almeno 1 carattere speciale - non dimenticare di considerare lo spazio anche come caratteri speciali]

almeno 10 caratteri

al massimo 128 caratteri

non più di 2 caratteri identici di fila (es. 111 non consentiti)

'^ (?!. (.) \ 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


0

Sfortunatamente tutto quanto sopra regex non ha funzionato per me. Le regole di base di una password complessa sono

  • Dovrebbe contenere almeno una lettera maiuscola
  • Dovrebbe contenere almeno una piccola lettera
  • Dovrebbe contenere almeno un numero
  • Dovrebbe contenere almeno un carattere speciale
  • E lunghezza minima

Quindi, Best Regex sarebbe

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

Il regex sopra ha una lunghezza minima di 8. Puoi cambiarlo da {8,} a { any_number ,}

Modifica nelle regole?

diciamo che vuoi un minimo di x caratteri minuscoli, y caratteri maiuscoli, z caratteri numeri, lunghezza minima totale w . Quindi prova sotto regex

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

Nota: modifica x , y , z , w in regex

Modifica: risposta regex aggiornata

Modifica2: aggiunta modifica


La vostra espressione regolare è la corrispondenza 12345678Sei sicuro che è un forte password? Per favore, prova il tuo regex prima di pubblicare.
Tot

Va meglio ma non risponde alla domanda, vogliono 1) lunghezza di 8 caratteri. 2) 2 lettere in maiuscolo. 3) 1 carattere speciale (! @ # $ & *). 4) 2 cifre (0-9). 5) 3 lettere in minuscolo.
Tot

@Toto Puoi per favore condividere i tuoi pensieri ora?
Juned Khatri,

La tua regex non tiene conto del fatto che le 2 lettere maiuscole obbligatorie potrebbero essere separate con altri caratteri, stessa osservazione per le lettere minuscole e le cifre. La risposta valida è quella che è stata accettata.
Tot
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.