157
votes

My password strength criteria is as below :

  • 8 characters length
  • 2 letters in Upper Case
  • 1 Special Character (!@#$&*)
  • 2 numerals (0-9)
  • 3 letters in Lower Case

Can somebody please give me regex for same. All conditions must be met by password .

11
Are you really willing to trust your password security measures to the Internet at large?Borealid
@Borealid: publishing your password policies should usually not significantly impact your security. If it does, then your policies are bad ("Only password and hello123 are valid passwords!").Joachim Sauer
@Joachim Sauer: That's not what I meant. What I meant was that the poster is probably just going to trust whatever regex he receives. Not such a good idea.Borealid
Actually this regex is going to be in service code , i will be testing for diff cases not blindly trust it :)Ajay Kelkar
Complex password rules will usually not lead to more safe passwords, important is only a minimum length. People cannot remember tons of strong passwords, and such rules can interfere with good password schemes. People can get very inventive to bypass such rules, e.g. by using weak passwords like "Password-2014". Often you end up with weaker passwords instead of stronger ones.martinstoeckli

11 Answers

483
votes

You can do these checks using positive look ahead assertions:

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

Rubular link

Explanation:

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

You can use zero-length positive look-aheads to specify each of your constraints separately:

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

If your regex engine doesn't support the \p notation and pure ASCII is enough, then you can replace \p{Lu} with [A-Z] and \p{Ll} with [a-z].

9
votes

Answers given above are perfect but I suggest to use multiple smaller regex rather than a big one.
Splitting the long regex have some advantages:

  • easiness to write and read
  • easiness to debug
  • easiness to add/remove part of regex

Generally this approach keep code easily maintainable.

Having said that, I share a piece of code that I write in Swift as example:

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

You should also consider changing some of your rules to:

  1. Add more special characters i.e. %, ^, (, ), -, _, +, and period. I'm adding all the special characters that you missed above the number signs in US keyboards. Escape the ones regex uses.
  2. Make the password 8 or more characters. Not just a static number 8.

With the above improvements, and for more flexibility and readability, I would modify the regex to.

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

Basic Explanation

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

Detailed Explanation

^                               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

And lastly, for testing purposes here is a robulink with the above regex

5
votes

I would suggest adding

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

All of above regex unfortunately didn't worked for me. A strong password's basic rules are

  • Should contain at least a capital letter
  • Should contain at least a small letter
  • Should contain at least a number
  • Should contain at least a special character
  • And minimum length

So, Best Regex would be

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

The above regex have minimum length of 8. You can change it from {8,} to {any_number,}

Modification in rules?

let' say you want minimum x characters small letters, y characters capital letters, z characters numbers, Total minimum length w. Then try below regex

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

Note: Change x, y, z, w in regex

Edit: Updated regex answer

Edit2: Added modification

1
votes

codaddict's solution works fine, but this one is a bit more efficient: (Python syntax)

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)

The negated character classes consume everything up to the desired character in a single step, requiring zero backtracking. (The dot star solution works just fine, but does require some backtracking.) Of course with short target strings such as passwords, this efficiency improvement will be negligible.

1
votes
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))
0
votes

For PHP, this works fine!

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

in this case the result is true

Thsks for @ridgerunner

0
votes

Another solution:

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
votes

Password must meet at least 3 out of the following 4 complexity rules,

[at least 1 uppercase character (A-Z) at least 1 lowercase character (a-z) at least 1 digit (0-9) at least 1 special character — do not forget to treat space as special characters too]

at least 10 characters

at most 128 characters

not more than 2 identical characters in a row (e.g., 111 not allowed)

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

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

(?=.[a-z])(?=.[A-Z])(?=.*[0-9])

(?=.[a-z])(?=.[A-Z])(?=.*[^a-zA-Z0-9])

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

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

.{10.127}