Regex: Strong password
Each (?=...) is a lookahead: it checks that something appears somewhere ahead without moving forward, so four of them in a row test four rules against the same string. Then .{12,} requires the length.
Pattern
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9]).{12,}$ How it works
- ^
- Start of the string.
- (?=.*[a-z])
- Somewhere ahead there is a lower-case letter.
- (?=.*[A-Z])
- An upper-case letter.
- (?=.*\d)
- A digit.
- (?=.*[^A-Za-z0-9])
- A character that is not a letter or a digit.
- .{12,}
- Twelve or more characters, which the lookaheads did not consume.
- $
- End of the string.
Matches
- Tr0ub4dor&3xyz
- correct-Horse-9-battery
- Zz9!aaaaaaaa
Doesn't match
- password123!
- SHORT1!a
- NoDigitsHere!!
- Abcdefghijk12
In your language
- JavaScript
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9]).{12,}$/A literal;
new RegExp(source, flags)builds the same from a string.- Python
re.compile(r"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9]).{12,}$", re.ASCII)A raw string, so backslashes reach
reas written.re.ASCIIkeeps\dto0-9, as in JavaScript (Python matches any Unicode digit otherwise). Usere.fullmatchto test a whole string.- Java
Pattern.compile("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[^A-Za-z0-9]).{12,}$")A normal string literal, so every backslash is doubled.
matcher(s).matches()tests the whole string.- Go
-
RE2 has no lookahead. Check each rule with its own
MatchStringcall, and the length withutf8.RuneCountInString(s) >= 12(lencounts bytes). - PHP
preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9]).{12,}$/D', $input)PCRE with
/delimiters inside a single-quoted string. Without theDmodifier,$also matches before a final newline, so it is added to patterns that end in$.- C#
new Regex(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9]).{12,}$", RegexOptions.ECMAScript)A verbatim string: backslashes stay, a quote is doubled.
RegexOptions.ECMAScriptkeeps\dto0-9, as in JavaScript.$also matches before a final newline; to reject one, end the pattern with\zinstead.
Common mistakes
Length beats composition rules
NIST SP 800-63B advises against rules like these, which push people towards
Password1!. It recommends a minimum length, room for long passphrases, and checking new passwords against lists of breached ones.A pattern cannot know a password leaked
Password123!passes all four rules and appears in every breach list. Check candidates against such a list, for instance the Have I Been Pwned range API.The dot skips line breaks
Without the
sflag,.does not match a newline, so a pasted password that ends in a line break fails the length check. Trim the input first.