Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP regular expression for strong password validation [duplicate]

I've seen the following regular expression around the web.

(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$

It validates only if the string:

   * contain at least (1) upper case letter
   * contain at least (1) lower case letter
   * contain at least (1) number or special character
   * contain at least (8) characters in length

I'd like to know how to convert this regular expression so that it checks the string to

* contain at least (2) upper case letter
* contain at least (2) lower case letter
* contain at least (2) digits
* contain at least (2) special character
* contain at least (8) characters in length

Well, if it contains at least 2 upper,lower,digits and special characters then I wouldn't need the 8 characters length.

Special characters include:

`~!@#$%^&*()_-+=[]\|{};:'".,/<>?

like image 524
Jason Avatar asked Apr 14 '10 13:04

Jason


3 Answers

I have to agree with Alan. If the existing regex is so complicated, why try and do it in just one regex?

Just break it down into approachable simple steps. You have already done that.

Now write 4 regex to validate your parts, add basic logic to the 4 regex and measure the length of the string. Done.

Which would you rather debug, this:

(?=^(?:[^A-Z]*[A-Z]){2})(?=^(?:[^a-z]*[a-z]){2})(?=^(?:\D*\d){2})(?=^(?:\w*\W){2})^[A-Za-z\d\W]{8,}$ (which does not work btw...)

or this:

function valid_pass($candidate) {
   $r1='/[A-Z]/';  //Uppercase
   $r2='/[a-z]/';  //lowercase
   $r3='/[!@#$%^&*()\-_=+{};:,<.>]/';  // whatever you mean by 'special char'
   $r4='/[0-9]/';  //numbers

   if(preg_match_all($r1,$candidate, $o)<2) return FALSE;

   if(preg_match_all($r2,$candidate, $o)<2) return FALSE;

   if(preg_match_all($r3,$candidate, $o)<2) return FALSE;

   if(preg_match_all($r4,$candidate, $o)<2) return FALSE;

   if(strlen($candidate)<8) return FALSE;

   return TRUE;
}

Why folks feel they have to write a regex that no one can understand just so they can do it in one go is beyond me...


Ok ok -- if you really want a single regex, learn about lookaheads to validate your rules.

This monster does what you asked in one go:

^                                        # start of line
(?=(?:.*[A-Z]){2,})                      # 2 upper case letters
(?=(?:.*[a-z]){2,})                      # 2 lower case letters
(?=(?:.*\d){2,})                         # 2 digits
(?=(?:.*[!@#$%^&*()\-_=+{};:,<.>]){2,})  # 2 special characters
(.{8,})                                  # length 8 or more
$                                        # EOL 

Demo

like image 61
dawg Avatar answered Nov 18 '22 06:11

dawg


The best way to adapt that regex is to chuck it out and write some code instead. The required regex would be so long and complicated, you wouldn't be able to read it two hours after you wrote it. The equivalent PHP code will be tedious, but at least you'll be able understand what you wrote.

This isn't meant as a slam on you, by the way. Regexes are just barely suitable for password-strength validation in most cases, but your requirements are more complicated than usual, and it's just not worth it. Also, that regex you posted is crap. Never trust regexes you find floating around the web. Or any code, for that matter. Or, heck, anything. :-/

like image 31
Alan Moore Avatar answered Nov 18 '22 04:11

Alan Moore


If you really want to use a regular expression, try this:

(?=^(?:[^A-Z]*[A-Z]){2})(?=^(?:[^a-z]*[a-z]){2})(?=^(?:\D*\d){2})(?=^(?:\w*\W){2})^[A-Za-z\d\W]{8,}$

Some explanation:

  • (?=^(?:[^A-Z]*[A-Z]){2}) tests for two repetitions of [^A-Z]*[A-Z] which is a sequence of zero or more characters except uppercase letters followed by one uppercase letter
  • (?=^(?:[^a-z]*[a-z]){2}) (same as above with lowercase letters)
  • (?=^(?:\D*\d){2}) (same as above with digits)
  • (?=^(?:\w*\W){2}) (same as above with non-word characters, but you may change \W with a character class of whatever special characters you want)
  • ^[A-Za-z\d\W]{8,}$ tests the length of the whole string consisting only of character of the union of all other character classes.
like image 16
Gumbo Avatar answered Nov 18 '22 04:11

Gumbo