2

I'm trying to create a regex validation for a password which is meant to be:

  1. 6+ characters long
  2. Has at least one a-z
  3. Has at least one A-Z
  4. Has at leat one 0-9

So, in other words, the match will have :

  1. at least one a-z, A-Z, 0-9
  2. at least 3 any other characters

I've came up with:

((.*){3,}[a-z]{1,}[A-Z]{1,}[0-9]{1,})

it seems pretty simple and logical to me, but 2 things go wrong:

  1. quantifier {3,} for (.*) somehow doesn't work and destroys whole regex. At first I had {6,} at the end but then regex would affect the quantifiers in inner groups, so it will require [A-Z]{6,} instead of [A-Z]{1,}
  2. when I remove {3,} the regex works, but will match only if the groups are in order - so that it will match aaBB11, but not BBaa11
van_folmert
  • 4,257
  • 10
  • 44
  • 89
  • Take a look at [this](http://stackoverflow.com/questions/3466850/complex-password-regular-expression). You might change your mind about using regex to do what you want. – tgogos Jun 15 '14 at 18:05
  • I have found a very similar answered question: http://stackoverflow.com/a/14850765/1650907 – van_folmert Jun 15 '14 at 18:52
  • Please do a search first - this question gets asked (and answered) a LOT - e.g. [regex for password](http://stackoverflow.com/a/9611715/433790). I also recommend the following blog post: [Mastering Lookahead and Lookbehind](http://www.rexegg.com/regex-lookarounds.html) – ridgerunner Jun 15 '14 at 19:58
  • 1
    Possible duplicate of [Password REGEX with min 6 chars, at least one letter and one number and may contain special characters](http://stackoverflow.com/q/7844359/1255289) – miken32 Mar 22 '17 at 02:10

3 Answers3

8

This is a use case where I wouldn't use a single regular expression, but multiple simpler ones.

Still, to answer your question: If you only want to validate that the password matches those criteria, you could use lookaheads:

^(?=.{6})(?=.*?[a-z])(?=.*?[A-Z])(?=.*?[0-9])

You're basically looking for a position from which you look at

  • 6 characters (and maybe more to follow, doesn't matter): (?=.{6})
  • maybe something, then a lowercase letter: (?=.*?[a-z])
  • maybe something, then an uppercase letter: (?=.*?[A-Z])
  • maybe something, then a digit: (?=.*?[0-9])

The order of appearance is arbitrary due to the maybe something parts.

(Note that I've interpreted 6 characters long as at least 6 characters long.)

Marius Schulz
  • 15,976
  • 12
  • 63
  • 97
  • Good idea, since this is just what I need - a validating pattern, not a match for the password itself. However, I still don't know how to ignore order of matched groups... – van_folmert Jun 15 '14 at 18:20
  • 1
    Yeah, and if the question means *exactly 6 characters long* just change `(?=.{6})` to `^(?=.{6}$)` – Unihedron Jun 15 '14 at 18:21
  • An issue here is that `.` matches any character except newline (so the password might include spaces, tabs...) – tgogos Jun 15 '14 at 18:36
  • @antithesis: ...but not newlines. – Casimir et Hippolyte Jun 15 '14 at 18:41
  • Unfortunetely it doesn't work for input's `pattern` attribute – van_folmert Jun 15 '14 at 18:42
  • 1
    Your pattern is correct, however if you don't anchor it at the begining of the string with `^`, with an invalid password, each lookahead will be tested at each position in the string (i.e. from the start, after the first character, after the second, etc.). With the `^` the pattern will fail when the first lookahead fails, and lookeads are tested zero or one time only. – Casimir et Hippolyte Jun 15 '14 at 18:52
  • Example with `abcdefghij`: (anchored: 22 steps before failing, without anchor: 103 steps), with `abcdefghijabcdefghij` (a:32s, wa: 358s), and the number of steps for the pattern without anchor to fail is growing exponentially with the string length. – Casimir et Hippolyte Jun 15 '14 at 19:17
  • Among other problems, right now this would allow this for a password: `my robot is called R2d2` – zx81 Jun 15 '14 at 19:49
  • @CasimiretHippolyte Of course, silly me. Thanks for pointing out `^`. – Marius Schulz Jun 15 '14 at 21:38
2

I believe this is what you want:

^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[!-~]{6,}$

If we follow your spec to the letter, your validation password looks like this:

^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).{6,}$

However, we need to improve on this, because apart from the number, lower-case and upper-case letter, are you really willing to accept any character? For instance, can the user use a character in the Thai language? A space character? A tab? Didn't think so. :)

If you want to allow all the printable ASCII characters apart from space, instead of a dot, we can use this character range: [!-~]

How does it work?

  • The ^ anchor makes sure we start the match at the start of the string
  • The (?=.*[a-z]) lookahead ensures we have a lower-case character
  • The (?=.*[A-Z]) lookahead ensures we have an upper-case character
  • The (?=.*[0-9]) lookahead ensures we a digit
  • The (?=.*[a-z]) lookahead ensures we have a lower-case character
  • The [!-~]{6,} matches six or more ASCII printable ASCII characters that are not space.
  • The $ ensures we have reached the end of the string (otherwise, the password could contain more characters that are not allowed).
zx81
  • 41,100
  • 9
  • 89
  • 105
1

you could use this pattern ^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).{6,}

alpha bravo
  • 7,838
  • 1
  • 19
  • 23