简体   繁体   中英

What is wrong with this PHP regex to do password validation?

ereg("/^(?=.*[a-z])(?=.*[0-9])(?=.*[^a-zA-Z0-9])(?=.*[A-Z]).{7,19}$/","ABCabc123!!");

This is supposed to be a password validator, requiring alphabets in upper and lowercases along with numbers special chars and mininmum length of 8....but the above returns false. What am I doing wrong?

Use preg_* instead, and a better validation string

There are (at least) 3 issues with the regex you're using:

  1. You're currently needlessly checking for (?=.*[^a-zA-Z0-9]) when there is a better option to do this - [\\W_] .
  2. You're checking for at least 7 characters and no more than 19 , rather than at least 8 .
  3. You're using a deprecated function .
  4. Your function allows whitespace in passwords.

This should work better for you:

$regex = "/^\S*(?=\S*[a-z])(?=\S*[A-Z])(?=\S*[\d])(?=\S*[\W_])(?=\S{8,})\S*$/";
$valid = (bool) preg_match($regex,$password);

Explanation of the components of this regex:

/            Delimiter
^            Start of string anchor
\S*          Any string without whitespace
(?=\S*[a-z]) Must contain at least 1 lowercase letter
(?=\S*[A-Z]) Must contain at least 1 uppercase letter
(?=\S*[\d])  Must contain at least 1 digit
(?=\S*[\W_]) Must contain at least 1 special character
             (note: \W will not consider underscore '_' a special character)
(?=\S{8,})   Must contain at least 8 characters
$            End of string anchor

As pointed out by Andy Lester, you may be better off with multiple checks

As Andy mentioned, you're best off storing a bunch of rules. This allows you to tailor your error messages and add rules easily. In PHP I'd implement this in this way:

function validatePassword($password) {
    $rules = array(
        'no_whitespace' => '/^\S*$/',
        'match_upper'   => '/[A-Z]/',
        'match_lower'   => '/[a-z]/',
        'match_number'  => '/\d/',
        'match_special' => '/[\W_]/',
        'length_abv_8'  => '/\S{8,}/'
    );

    $valid = true;
    foreach($rules as $rule) {
        $valid = $valid && (bool) preg_match($rule, $password);
        if($valid !== true) break;
    }

    return (bool) $valid;
}

Live demonstration can be found here .

Don't try to do it all in one regex. Make multiple regex checks.

I know you're writing PHP, but I know Perl better, so follow along and get the idea.

my $password_is_valid =
    length($pw) >= 8 &&  # Length >= 8
    ($pw =~ /[a-z]/) &&  # Has lowercase
    ($pw =~ /[A-Z]/) &&  # Has uppercase
    ($pw =~ /\W/);       # Has special character

Sure, that takes up five lines instead of one, but in a year when you go back and have to add a new rule, or figure out what the code does, you'll be glad you wrote it that way. Maybe you require a digit later on. Easy!

my $password_is_valid =
    length($pw) >= 8 &&  # Length >= 8
    ($pw =~ /\d/)    &&  # Has digit
    ($pw =~ /[a-z]/) &&  # Has lowercase
    ($pw =~ /[A-Z]/) &&  # Has uppercase
    ($pw =~ /\W/);       # Has special character

Just because you can do it in one regex doesn't mean you should .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM