0

I have searched the site and not finding exactly what I am looking for. Password Criteria:

  1. Must be 6 characters, 50 max
  2. Must include 1 alpha character
  3. Must include 1 numeric or special character

Here is what I have in java:

public static Pattern p = Pattern.compile(
 "((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])|(?=.*[\\d~!@#$%^&*\\(\\)_+\\{\\}\\[\\]\\?<>|_]).{6,50})"
);

The problem is that a password of 1234567 is matching(it is valid) which it should not be.

Any help would be great.

wroniasty
  • 7,884
  • 2
  • 32
  • 24
tjforce
  • 35
  • 1
  • 5
  • 5
    I would never use a RegEx to validate a password. It is plain better and faster to validate it manually. – Luiggi Mendoza May 13 '13 at 17:46
  • 4
    Create 3 regex one for each case and validate then all together via AND operation. – br araujo May 13 '13 at 17:47
  • 4
    You might want to break up the requirements. Especially if you plan on informing the user as to why their password did not meet the requirements. If you put it all into one statement, you can't tell which requirement was unmet, and cannot tell them the specific reason. – BLuFeNiX May 13 '13 at 17:49
  • 1
    Pls look at -- http://stackoverflow.com/questions/3387785/password-validation-regular-expression and http://stackoverflow.com/questions/3466850/complex-password-regular-expression/3466868#3466868 – Bill May 13 '13 at 17:50

6 Answers6

4

I wouldn't try to use a single regular expression to do that. Regular expressions tend not to perform well when they get long and complicated.

boolean valid(String password){
    return password != null &&
    password.length() >= 6 &&
    password.length() <= 50 &&
    password.matches(".*[A-Za-z].*") &&
    password.matches(".*[0-9\\~\\!\\@\\#\\$\\%\\^\\&\\*\\(\\)_+\\{\\}\\[\\]\\?<>|_].*");
}
Lanaru
  • 9,421
  • 7
  • 38
  • 64
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
  • Additionally, your code will be more maintainable. Few people can just glance at a complex regex and understand what it's doing. –  May 13 '13 at 18:17
1

A regular expression can only match languages which can be expressed as a deterministic finite automaton, i.e. which doesn't require memory. Since you have to count special and alpha characters, this does require memory, so you're not going to be able to do this in a DFA. Your rules are simple enough, though that you could just scan the password, determine its length and ensure that the required characters are available.

Joel
  • 5,618
  • 1
  • 20
  • 19
  • You are confusing between theoretical regular expression and the "regular expression" that is used in programming languages. The "regular expression" in Java/.NET/PHP are not exactly regular expression in theoretical sense, so they are much more powerful than the theoretical regular expression. – nhahtdh May 13 '13 at 17:55
  • The only meaningful addition is backtracking, which doesn't permit full context free languages I would think, and almost certainly doesn't overcome this issue. – Joel May 13 '13 at 18:04
  • Java regex does not permit full context free language, but .NET regex and PHP regex can do bracket matching correctly. – nhahtdh May 13 '13 at 18:08
1

I'd suggest you to separate characters and length validation:

boolean checkPassword(String password) {
    return password.length() >= 6 && password.length() <= 50 && Pattern.compile("\\d|\\w").matcher(password).find();
}
AlexR
  • 114,158
  • 16
  • 130
  • 208
1

Make sure you use Matcher.matches() method, which assert that the whole string matches the pattern.

Your current regex:

"((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])|(?=.*[\\d~!@#$%^&*\\(\\)_+\\{\\}\\[\\]\\?<>|_]).{6,50})"

means:

  • The string must contain at least a digit (?=.*\\d), a lower case English alphabet (?=.*[a-z]), and an upper case character (?=.*[A-Z])
  • OR | The string must contain at least 1 character which may be digit or special character (?=.*[\\d~!@#$%^&*\\(\\)_+\\{\\}\\[\\]\\?<>|_])
  • Either conditions above holds true, and the string must be between 6 to 50 characters long, and does not contain any line separator.

The correct regex is:

"(?=.*[a-zA-Z])(?=.*[\\d~!@#$%^&*()_+{}\\[\\]?<>|]).{6,50}"

This will check:

  • The string must contain an English alphabet character (either upper case or lower case) (?=.*[a-zA-Z]), and a character which can be either a digit or a special character (?=.*[\\d~!@#$%^&*()_+{}\\[\\]?<>|])
  • The string must be between 6 and 50 characters, and does not contain any line separator.

Note that I removed escaping for most characters, except for [], since {}?() loses their special meaning inside character class.

nhahtdh
  • 55,989
  • 15
  • 126
  • 162
0

I would suggest splitting into separate regular expressions

$re_numbers = "/[0-9]/";
$re_letters = "/[a-zA-Z]/";

both of them must match and the length is tested separately, too. The code looks quite cleaner then and is easier to understand/change.

0

This way too complex for such a simple task:

Validate length using String#length()

password.length() >= 6 && password.length() <= 50

Validate each group using Matcher#find()

Pattern alpha = Pattern.compile("[a-zA-Z]");
boolean hasAlpha = alpha.matcher(password).find();
Pattern digit = Pattern.compile("\d");
boolean hasDigit = digit.matcher(password).find();
Pattern special = Pattern.compile("[\\~\\!\\@\\#\\$\\%\\^\\&\\*\\(\\)_+\\{\\}\\[\\]\\?<>|_]");
boolean hasSpecial = special.matcher(password).find();
hoaz
  • 9,883
  • 4
  • 42
  • 53