1

I want to use java-script to validate following criteria for password.

a.A password of at least 8 and no more than 24 characters is required.

b.Every password must contain at least three of these four types of characters:

1.an upper case letter

2.a lower case letter

3.a number

4.a special character.

I have found this code which is really easy and hand-full but it is just checking all 4 conditions not just at-least 3 conditions out of 4.

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

I will really appreciate if you help me to figure out to create javascript validation on password to full fill my above requirement.

Thank you all for your help. I have modified @Ehtesham code and achieved the functionality.

function isValidPassword(pass) {
    var lowerRegex = /[a-z]/;
    var upperRegex = /[A-Z]/;
    var numberRegex = /[0-9]/;
    var specialRegex = /[$@$!%*?&]/;
    var count = 0;
    if (pass.length < 8 || pass.length > 24) {
        return false;
    }
    if (pass.match(lowerRegex)){count += 1;}
    if (pass.match(upperRegex)){count += 1;}
    if (pass.match(numberRegex)){count += 1;}
    if (pass.match(specialRegex)){count += 1;}    
    if (count >= 3){
        return true;
    }else{
        return false;   
    }
}
orbnexus
  • 737
  • 1
  • 9
  • 38
  • 7
    Why is there an upper limit on the password length? Are you not hashing them in your database? – Paul S. Mar 05 '15 at 20:05
  • 6
    Split this into 4 regexes, and test them separately. – Lucas Trzesniewski Mar 05 '15 at 20:05
  • Why the low upper limit? – mbomb007 Mar 05 '15 at 20:09
  • Well it is a requirement from client and i have to do it – orbnexus Mar 05 '15 at 20:13
  • 2
    For what it's worth, you should advise your client that an upper limit is a really bad idea. – brandonscript Mar 05 '15 at 20:23
  • 2
    Somebody always asks this question. It can't be done using JavaScript regex because it doesn't do conditionals. If it did conditionals, the regex is easy. 3/4, 2/9, 10/30, it's always the same format `/^(?:.*?(?:((?(1)(?!))[A-Z]+)|((?(2)(?!))[a-z]+)|((?(3)(?!))[0-9]+)|((?(4)(?!))[!@#$%^&*()\[\]_+}{?><,.\/":;'-]+))){3}.*$/` –  Mar 05 '15 at 20:25
  • @sln nice trick, but you can omit the `+` quantifiers ;) – Lucas Trzesniewski Mar 05 '15 at 20:32
  • @LucasTrzesniewski - It benchmarks quicker using the quantifiers. And faster still if the main group is atomic. –  Mar 05 '15 at 20:34
  • @sln that's quite surprising for the quantifiers... which engine did you use? I'd also say that `.*$` is redundant. – Lucas Trzesniewski Mar 05 '15 at 20:35
  • @LucasTrzesniewski - Not surprising considering that it matches out of order, sort of helps it along. –  Mar 05 '15 at 20:37
  • @LucasTrzesniewski - I benched it in Perl. The `.*$` was a cut'n paste hang over from a different version, still, I'm using it as a visual aid testing regex using [this](http://www.regexformat.com). They released a fully functional trial version if you want to try it out. –  Mar 05 '15 at 20:41
  • 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-2015". Often you end up with weaker passwords instead of stronger ones. – martinstoeckli Mar 06 '15 at 07:48

4 Answers4

4

The regex you provided is made out of 5 major parts:

  • The pattern validating the length: [A-Za-z\d$@$!%*?&]{8,24}
  • A positive lookahead for at least one lowercase: (?=.*[a-z])
  • A positive lookahead for at least one uppercase: (?=.*[A-Z])
  • A positive lookahead for at least one number: (?=.*\d)
  • A positive lookahead for at least one special char: (?=.*[$@$!%*?&])

Now, you only want to apply 3 out of the 4 positive lookaheads. Since lookaheads are non-consuming matches, the cursor of the regex-engine will remain unchanged, as the matching is going on. (Using positive Lookaheads this way is often used to generate AND-Patterns)

So, you now have 4 conditions and you want that only 3 of them are matched. As described, it would be easy to use independent expressions and check if 3 apply. However, some native features (for instance jsf's f:validateRegex) only work with a single pattern.

Regular Expressions are supporting OR in a native way: | - hence to turn your expression 1 AND 2 AND 3 AND 4 into a minimum requirement of matching 3 of them, you could use an expression like (1 AND 2 AND 3) OR (1 AND 2 AND 4) OR (1 AND 3 AND 4) OR (2 AND 3 AND 4), which would cover all usecases required:

1   2   3   4
1   1   1   0
1   1   0   1
1   0   1   1
0   1   1   1

So, to match all this within a single pattern, just rearange your lookaheads as required:

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

Drilldown:

^(?:                                     - non matching group
  (?=.*[a-z])(?=.*[A-Z])(?=.*\d)         - one lower, one upper, one number
  |                                      - or
  (?=.*[a-z])(?=.*[A-Z])(?=.*[$@$!%*?&]) - one lower, one upper, one special
  |                                      - or
  (?=.*[a-z])(?=.*\d)(?=.*[$@$!%*?&])    - one lower, one number, one special  
  |                                      - or
  (?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])    - one upper, one number, one special
 )
 [A-Za-z\d$@$!%*?&]{8,24}$               - 8 to 24 chars.

(Debuggex is using javascript)

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

Regular expression visualization

Debuggex Demo

dognose
  • 20,360
  • 9
  • 61
  • 107
  • Permutations is the only way. What would it look like with 4 out of 5 ? –  Mar 05 '15 at 20:54
  • @sln Permutations can be generated easily (little code). Not a real problem. – dognose Mar 05 '15 at 20:55
  • Will get strange with 3 of 5. –  Mar 05 '15 at 21:00
  • @sln ofc. it becomes hard to read - but if the scenario requires a single pattern, there is no other option. – dognose Mar 05 '15 at 21:03
  • That's awesome work, @dognose. A regex this complicated speaks to just how much regex should not be used to solve the OP's problem. At least, not solving the *whole* problem in a single regex. – aliteralmind Mar 05 '15 at 21:14
  • Why do you have the first non-matching group? And won’t your code break if the order of matches changes? For example, I think this will not match in your example even thought it should `Password99`. Because here uppercase is before lowercase and you don’t have that in your pattern. Don’t you need to permute all the possibilities including all the possible sequences? – Adriano_Pinaffo Nov 15 '20 at 21:39
2

Please consider bookmarking the Stack Overflow Regular Expressions FAQ for future reference. There is a password validation entry under

"Common validation tasks > Internet"

That said, this seems like a pretty easy task if you break it up, as others have suggested. Mashing up all those requirements into a single regex, although an interesting exercise, is overkill in my opinion.

(This is Java, but there are equivalent concepts in JavaScript.)

public bolean isPasswordValid(String password)  {
   if(!length in bounds)  {
       return  false;
   }

   boolean hasUpper = Pattern.compile("[a-z]").find(password);
   boolean hasLower = Pattern.compile("[A-Z]").find(password);
   boolean hasDigit = Pattern.compile("[0-9]").find(password);
   boolean hasSpecialChar = Pattern.compile("...NOT SURE OF THIS ONE...").find(password);

   int types = (hasUpper ? 1 : 0) + (hasLower ? 1 : 0) +
               (hasDigit ? 1 : 0) + (hasSpecialChar ? 1 : 0);

   return  (types >= 3);
}

And if this is a function that will be used rapid fire, then you'll likely want to pre-compile and store those Matchers.

Community
  • 1
  • 1
aliteralmind
  • 19,847
  • 17
  • 77
  • 108
1

In javascript you can use following simple function.

function isValidPassword(pass) {
    var lowerRegex = /[a-z]/;
    var upperRegex = /[A-Z]/;
    var numberRegex = /[0-9]/;
    var specialRegex = /[$@$!%*?&]/;

    if (pass.length < 8 || pass.length > 24) {
        return false;
    }

    if (pass.match(lowerRegex) && pass.match(upperRegex) &&     
        pass.match(numberRegex) && pass.match(specialRegex)) {
        return true;
    }

    return false;
}

Jsfiddle demo

Ehtesham
  • 2,967
  • 1
  • 18
  • 20
  • Basically my answer converted to JavaScript, which is fine. The requirement is "at least three" special char types, not "all four". – aliteralmind Mar 05 '15 at 22:40
0

This builds on Ehtesham's answer to require 3 out of 4:

function isValidPassword(pass) {
    var lowerRegex   = /[a-z]/;
    var upperRegex   = /[A-Z]/;
    var numberRegex  = /[0-9]/;
    var specialRegex = /[$@$!%*?&]/;
    var mustBe3 = 0;

    if(pass.length < 9 || pass.length > 24) { return false; }
    if(pass.match(lowerRegex))  { mustBe3 ++; }
    if(pass.match(upperRegex))  { mustBe3 ++; }
    if(pass.match(numberRegex)) { mustBe3 ++; }
    if(pass.match(specialRegex)){ mustBe3 ++; }

    // for testing ...
    if(window.console) console.log('pass: '+pass+' mustBe3: '+mustBe3);

    if( mustBe3 >= 3 ) { return true; }
    return false;
}
bcre8ve
  • 1
  • 1