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

后端 未结 2 755
感情败类
感情败类 2020-12-20 06:49
ereg(\"/^(?=.*[a-z])(?=.*[0-9])(?=.*[^a-zA-Z0-9])(?=.*[A-Z]).{7,19}$/\",\"ABCabc123!!\");

This is supposed to be a password validator, requiring al

相关标签:
2条回答
  • 2020-12-20 07:39

    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.

    0 讨论(0)
  • 2020-12-20 07:49

    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.

    0 讨论(0)
提交回复
热议问题