Password complexity regex with number or special c

2019-07-06 21:31发布

问题:

I've got some regex that'll check incoming passwords for complexity requirements. However it's not robust enough for my needs.

((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,20})

It ensures that a password meets minimum length, contains characters of both cases and includes a number.

However I need to modify this so that it can contain a number and/or an allowed special character. I've even been given a list of allowed special characters.

I have two problems, delimiting the special characters and making the first condition do an and/or match for number or special.

I'd really appreciate advice from one of the regex gods round these parts.

The allowed special characters are: @%+\/'!#$^?:.(){}[]~-_

回答1:

If I understand your question correctly, you're looking for a possibility to require another special character. This could be done as follows (see the last lookahead):

((?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!§$%&/(/)]).{8,20})

See a demo for this approach here on regex101.com.
However, you can make your expression even better with further approvements: the dot-star (.*) brings you down the line and backtracks afterwards. If you have a password of say 10 characters and you want to make sure, four lookaheads need to be fulfilled, you'll need at least 40 steps (even more as the engine needs to backtrack).
To optimize your expression, you could use the exact opposite of your required characters, thus making the engine come to an end faster. Additionally, as already pointed out in the comments, do not limit your maximum password length.
In the language of regular expressions, this would come down to:

((?=\D*\d)(?=[^a-z]*[a-z])(?=[^A-Z]*[A-Z])(?=.*[!§$%&/(/)]).{8,})

With the first approach, 63 steps are needed, while the optimized version only needs 29 steps (the half!). Regarding your second question, allowing a digit or a special character, you could simply use an alternation (|) like so:

((?:(?=\D*\d)|(?=.*[!§$%&/(/)]))(?=[^a-z]*[a-z])(?=[^A-Z]*[A-Z]).{8,})

Or put the \d in the brackets as well, like so:

((?=[^a-z]*[a-z])(?=[^A-Z]*[A-Z])(?=.*[\d!§$%&/(/)]).{8,})

This one would consider to be ConsideredAgoodPassw!rd and C0nsideredAgoodPassword a good password.