197

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 .

Alan Moore
  • 73,866
  • 12
  • 100
  • 156
Ajay Kelkar
  • 4,591
  • 4
  • 30
  • 29
  • 2
    Are you really willing to trust your password security measures to the Internet at large? – Borealid Feb 28 '11 at 12:47
  • 19
    @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 Feb 28 '11 at 12:50
  • 3
    @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 Feb 28 '11 at 12:51
  • 4
    Actually this regex is going to be in service code , i will be testing for diff cases not blindly trust it :) – Ajay Kelkar Feb 28 '11 at 15:08
  • 11
    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 Oct 24 '14 at 12:28
  • @martinstoeckli +1 Agreed. I've seen in the past strong password schemes and it's often led to weaker security due to people writing it down. Strong password schemes are fine with tech-savy users. LCDs (lowest common denominators ie normal people) need it kept simple. :) – Jag Nov 25 '15 at 11:47
  • 2
    Password rules are old. Please see [Reference - Password Validation](https://stackoverflow.com/questions/48345922/reference-password-validation) for more information. – ctwheels May 30 '18 at 15:30
  • One definitely shouldn't trust password strength enforcement only to client-side processes, BUT a method of codifying requirements could be used as hints to password generators operating with the browser, such as Apple's Keychain. Form input fields already have a regex based [PATTERN](https://html.spec.whatwg.org/multipage/input.html#attr-input-pattern) attribute, which can provide such hinting as well as giving client-side validation that would reduce server traffic. – TRT 1968 Feb 21 '22 at 14:54

13 Answers13

586

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.
Alan Moore
  • 73,866
  • 12
  • 100
  • 156
codaddict
  • 445,704
  • 82
  • 492
  • 529
  • 134
    For anyone who wants a length of at least `n`, replace `.{8}` with `.{n,}` – NullUserException Oct 15 '12 at 16:33
  • 26
    +1 for a complete explanation. My password rules are different but based on your answer I can adapt the regex. – Morvael Nov 12 '13 at 10:59
  • 18
    Thank you for describing whats happening in the regex. This serves as a great learning example for those of us who've never really got on with the syntax. –  Nov 22 '13 at 06:11
  • 4
    I also appreciate the explanation of the regex. To many times I use complex regex that I found, without really understanding what is going on. – Nicholas Smith Jan 27 '14 at 17:54
  • 2
    Positive look ahead is exactly the thing to use for this kind of things. Here's mine : ^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{8,24}$ (between 8 and 24 chars, at least one of each type among lowercase, uppercase, and numbers) – AFract Aug 31 '15 at 15:43
  • @AFract Why have the 24 character limit? Why have any maximum length limit? Longer passwords add more entropy, and allow for things like pass phrases. – Necoras Oct 13 '15 at 22:02
  • @Necoras : 24 was just an example. It can easily be removed or changed in my regex. I shared it just because it's way simpler than the one in accepted answer, it enforces less constraints for end user which can also be an advantage in "real life" situation (even if accepted answer is perfectly answering to original question!). But you're right about max password length. – AFract Oct 20 '15 at 14:03
  • Don't forget that you may need to escape any of those special chars which might be interpreted as part of the regex (e.g. * - ? etc) – Mike Nov 11 '15 at 15:24
  • I would argue for separate regexes for each condition; that way you can inform the user of the nature of his shortcomings (you didn't include a capital letter, you need to include a numeral, etc.) Better than a generic "bad password" message followed by a recitation of the rules. – Gullbyrd Jan 31 '16 at 21:03
  • This doesn't work: Allows '>' character at end of string if length is 8,20 http://regexstorm.net/tester?p=%5e(%3f%3d.*%5bA-Z%5d.*%5bA-Z%5d)(%3f%3d.*%5b!%40%23%24%26*%5d)(%3f%3d.*%5b0-9%5d.*%5b0-9%5d)(%3f%3d.*%5ba-z%5d.*%5ba-z%5d.*%5ba-z%5d).%7b8%2c20%7d%24&i=WWir11!!fd%3e – Pakk Sep 07 '16 at 18:31
  • 3
    for 1 upper case, 1 lower case , 1 numeral, 1 special character and min length of n characters:- ^(?=.*[A-Z])(?=.*[!@#$&*])(?=.*[0-9])(?=.*[a-z]).{n,}$ thanks a lot for the explanation – Ashish P Oct 19 '17 at 09:48
  • 10
    Great pattern, I wonder why not using quantifiers? At least 1 special, 1 number, 1 special char, 8 character: ^(?=.*([A-Z]){1,})(?=.*[!@#$&*]{1,})(?=.*[0-9]{1,})(?=.*[a-z]{1,}).{8,100}$ – RockOnGom Oct 15 '18 at 07:48
  • Starange that no one pointed that your answer accepts spaces too! Try increasing the accepted chars from 8 to 9 and add a space. – Kumar Ashutosh Apr 22 '19 at 10:43
  • @RockOnGom I think your suggestion is more proper because of flexibility. That is great but how can I do to avoid accepting SPACE in the string? – QMaster Aug 17 '20 at 20:34
  • I think this is proper for most cases: ^(?=.*[A-Z]{1,})(?=.*[a-z]{1,})(?=.*[0-9]{1,})(?=.*[~!@#$%^&*()\-_=+{};:,<.>]{1,}).{8,}$ – QMaster Aug 17 '20 at 21:39
  • @QMaster: I think that can work, but not sure about it's performance: ^(?=.*([A-Z]){1,})(?=.*[!@#$&*]{1,})(?=.*[0-9]{1,})(?=.*[a-z]{1,})([^ ]){8,100}$ – RockOnGom Aug 18 '20 at 06:50
  • @RockOnGom That does not work. Thanks anyway. I will be working on it. I'll appreciate if you share new results. – QMaster Aug 18 '20 at 19:24
  • I see loads of ^ start and $ end anchors used with these positive lookahead. why are those needed? in my testing i can leave those out – Fre Timmerman Mar 09 '22 at 10:39
  • @codaddict This does not prevent from adding other special characters. It will require at least one of the special characters but then you can also add other characters not in the list. – BernardA May 24 '23 at 09:50
37

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

lsu_guy
  • 1,525
  • 15
  • 12
  • 1
    Thanks @AFract. I'm using it in my code. I like readability and repeat-ability, for when you have to go back and change it in the future i.e. in case of a password policy change :) – lsu_guy Feb 25 '20 at 16:24
  • 1
    Fantastic explanation.This should be the accepted answer IMHO. – Mohamed Wagih Sep 03 '20 at 12:24
  • /^(?=.*[a-z]){3,}(?=.*[A-Z]){2,}(?=.*[0-9]){2,}(?=.*[!@#$%^&*()--__+.]){1,}.{8,}$/.test("aA1$bcde") return true with just 1 numeric and 1 capital – Priyank Bolia Sep 15 '20 at 08:21
  • Updated to include your test case @PriyankBolia. See new robulink, which should now work. – lsu_guy Sep 18 '20 at 19:56
  • @Isu_guy what about if you want it to FAIL if there if it exceeds Max occurances? is it just {min_occurances, max_occurances} ??? – ennth Oct 28 '20 at 12:50
  • correct. It should be {min_occurances, max_occurances}. Keep in mind, I haven't tested that, so please do so. Also haven't seen password policies where a max is specified. Could you share the use-case, which requires a max. Thanks. – lsu_guy Oct 29 '20 at 16:15
  • Thanks, you made it simple and easy to understand. I changed as per my requirement easily – Newton Suhail Jan 17 '22 at 08:56
  • can you convert it in golang? – San Jul 12 '22 at 03:42
  • @San - I'm not a go user. However go has a regexp package, which has matchstring. You should be able to use it by calling: matched, _ := regexp.MatchString(REGEX_STRING_FROM_ABOVE_INCLUDING_`, "PASSWORD_STRING") You can see more here: https://yourbasic.org/golang/regexp-cheat-sheet/ – lsu_guy Jul 12 '22 at 06:33
  • Doesn't accept `f6e056d2-4b9f-4715-89fc-8ae99fc270cb#11!aA` – Richard Tyler Miles Nov 29 '22 at 18:51
  • 1
    @RichardTylerMiles that's correct. Your string doesn't have 2 uppercase letters. If you added one more upper case letter, the regex will accept it. – lsu_guy Nov 30 '22 at 20:26
13

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 []
        }
    }
}
Luca Davanzo
  • 21,000
  • 15
  • 120
  • 146
  • Also, when using complex regex like above, it is very easy to open yourself to catastrophic backtracking (https://www.regular-expressions.info/catastrophic.html). This can go unnoticed until one day your server hangs with 100% CPU because a user used a "strange" password. Example: ^([a-z0-9]+){8,}$ (can you see the error?) – aKzenT Sep 22 '17 at 18:10
11

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

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
11

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

Juned Khatri
  • 451
  • 5
  • 8
5

I would suggest adding

(?!.*pass|.*word|.*1234|.*qwer|.*asdf) exclude common passwords
Stuart
  • 1,008
  • 11
  • 14
2

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.

ridgerunner
  • 33,777
  • 5
  • 57
  • 69
  • Could you please check if is it correct? I'm in doubt because of opening round bracket in first line between triple doublequote and question mark. I can see that Python comment (hash) is later. I cannot see correspondent closing round bracket near end anchor (dollar sign). Should mention I'm not a regex profy. – lospejos Nov 02 '16 at 16:51
  • @lospejos - The # is not the start of a regular one line comment. This hash is part of a _comment group_ which begins with a `(?#` and ends with a `)`. There are no unbalanced parens in this regex. – ridgerunner Jun 16 '17 at 15:45
2
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
  • 21
  • 1
1

codaddict gives a great answer:

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

But what if you want to have, let's say 8 lowercase letters instead of 3, writing something like this

(?=.*[a-z].*[a-z].*[a-z].*[a-z].*[a-z].*[a-z].*[a-z].*[a-z])

is really cumbersome, so instead you can write

(?=(?:.*[a-z]){8})

?: used to exclude the capture group

So I think a better answer would be:

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

rubular link

Note that (?=.*[a-z]{8}) will work differently, since it's a regex for 8 consecutive lowercase letters.

Jonathan
  • 23
  • 1
  • 5
1

the same expresion as the most vote answer but a bit shorter for easy write and understanding.

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

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

  • why not `return preg_match("/^(?=(?:[^A-Z]*[A-Z]){2})(?=(?:[^0-9]*[0-9]){2}).{8,}$/", 'CaSu4Li8')`? – aloisdg Nov 22 '18 at 14:18
0

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()
  • how do you do this but with a max range? so like two capital letters but NO MORE than two capital letters? or two numeric digits but NO MORE than two??? – ennth Oct 28 '20 at 13:14
0

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}

RaVi
  • 3
  • 3