This pattern would allow all characters that's not a digit or a-Z.

[^\da-zA-Z]

Regarding the \W it's a negated \w, which is the same as [A-Za-z0-9_]. Thus will \W be all characters that's not an english letter, digit or an underscore.

As I mentioned as a comment this is a great resource for learning regex. And here's a good site to test the regex.

Answer from Marcus on Stack Overflow
Top answer
1 of 6
21

You could split your regex into different checks.

It will allow you to write more readable conditions and to display specific error messages. Although, regexp patterns will be easier to write and to understand.

i.e. :

$errors = array();
if (strlen($pass) < 8 || strlen($pass) > 16) {
    $errors[] = "Password should be min 8 characters and max 16 characters";
}
if (!preg_match("/\d/", $pass)) {
    $errors[] = "Password should contain at least one digit";
}
if (!preg_match("/[A-Z]/", $pass)) {
    $errors[] = "Password should contain at least one Capital Letter";
}
if (!preg_match("/[a-z]/", $pass)) {
    $errors[] = "Password should contain at least one small Letter";
}
if (!preg_match("/\W/", $pass)) {
    $errors[] = "Password should contain at least one special character";
}
if (preg_match("/\s/", $pass)) {
    $errors[] = "Password should not contain any white space";
}

if ($errors) {
    foreach ($errors as $error) {
        echo $error . "\n";
    }
    die();
} else {
    echo "$pass => MATCH\n";
}

Hope it helps.

2 of 6
8

You can try this:

^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.* )(?=.*[^a-zA-Z0-9]).{8,16}$

It covers all your requirment

Explanation

  1. (?=.*\d) Atleast a digit
  2. (?=.*[a-z]) Atleast a lower case letter
  3. (?=.*[A-Z]) Atleast an upper case letter
  4. (?!.* ) no space
  5. (?=.*[^a-zA-Z0-9]) at least a character except a-zA-Z0-9
  6. .{8,16} between 8 to 16 characters

Sample Code:

<?php
/m';
$str = 'Jtuhn
12J@k
jok
Joan 12@45
Jghf2@45
Joan=?j123j
';

preg_match_all(str, $matches);
print_r($matches);

?>

Run it here

🌐
Imtiaz Epu
imtiazepu.com › password-validation
Password Validation with PHP and Regular Expressions
February 4, 2023 - $password = $_POST['password ']; if (preg_match("#.*^(?=.{8,20})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*W).*$#", $password )){ echo "Your password is strong."; } else { echo "Your password is not safe."; } You may use "d" instead of "[a-z]" and "W" instead of non-word characters, symbols...
🌐
SitePoint
sitepoint.com › php
Regex for Special Characters - PHP - SitePoint Forums | Web Development & Design Community
July 5, 2012 - // Check for Special-Character. if (empty($errors)){ if (!preg_match("#[\\~\\`\\!\\@\\#\\$\\%\\^\\&\\*\\(\\)\\_\\-\\+\\=\\{\\}\\[\\]\\|\\:\\;\\ \\.\\?\\/\\\\\\\\]+#", $newPass1)){ $errors['newPass'] = 'Password must have at least 1 Special Character.'; } } 1.) Someone told me I do NOT need ...
🌐
CodexWorld
codexworld.com › home › how to guides › how to validate password strength in php
How to Validate Password Strength in PHP - CodexWorld
January 6, 2022 - // Given password $password = ... strlen($password) < 8) { echo 'Password should be at least 8 characters in length and should include at least one upper case letter, one number, and one special character.'; }else{ echo 'Strong ...
🌐
www.code-helper.com
code-helper.com › answers › php-preg-match-special-characters
Php preg_match special characters
$string="sadw$" if(preg_match("/[\[^\'£$%^&*()}{@:\'#~?><>,;@\|\\\-=\-_+\-¬\`\]]/", $string)){ //this string contain atleast one of these [^'£$%^&*()}{@:'#~?><>,;@|\-=-_+-¬`] characters }
Find elsewhere
Top answer
1 of 3
2

You may need to escape some special characters used by the Regex Engine. And by the way, you could as well do that in one Go as shown below. Quick-Test Here.

<?php


    /*public static*/ function encPasswordCheckFailed($password) {
        // THIS READS:
        // IF THE PASSWORD DOES CONTAIN AN UPPER-CASE CHARACTER
        // AND ALSO DOES CONTAIN A LOWER-CASE CHARACTER
        // AND STILL DOES CONTAIN A NUMERIC CHARACTER
        // AND EVEN MORE, DOES CONTAIN ANY  OF THE SPECIFIED SPECIAL CHARACTERS 
        // RETURN FALSE OTHERWISE RETURN TRUE
        if (preg_match('/[A-Z]+/', $password) &&                    // CHECK FOR UPPERCASE CHARACTER
            preg_match('/[a-z]+/', $password) &&                    // AND CHECK FOR LOWERCASE CHARACTER
            preg_match('/[0-9]+/', $password)&&                     // AND CHECK FOR NUMERIC CHARACTER
            preg_match('/[\!@#$%\^&\*\(\)\-\+<>]+/', $password)     // AND CHECK FOR SPECIAL CHARACTER
        ) {
            return false;
        }

        return true;
    }

    var_dump( encPasswordCheckFailed("abcd123") );    //<== boolean true
    var_dump( encPasswordCheckFailed("abcD123") );    //<== boolean true
    var_dump( encPasswordCheckFailed("abcD1@23-") );  //<== boolean false
2 of 3
1

The dash/minus character is your issue, as when inside a character group, it denotes a character range.

You need to either:

  • Escape it with a backslash, so that it isn't treated as a special character:
    /[!@#$%^&*()\-+<>]+/
  • Or put it at the end of the pattern, so the PCRE engine knows it can't possibly denote a range:
    /[!@#$%^&*()+<>-]+/

Similarly, the caret (^) is also a special character, denoting negation when inside a character group, but because you didn't put it at the very first position inside the character group, the PCRE engine knows it has no special meaning in your case.

🌐
MetaProgrammingGuide
metaprogrammingguide.com › code › preg-match-special-characters
Php, Preg_match special characters
June 7, 2022 - Preg match - PHP preg_match special characters only, You need to match the string for any alphanumeric character.
Top answer
1 of 2
3

In no particular order:

  • That hashing method is terrible and insecure. Hashes must be costly (slow) most of all, and they must contain a unique salt. Use password_hash, don't invent your own.

    • If that is the required hashing method: move away from it ASAP. But at the very least, utf8_encode is entirely superfluous, since you're only allowing ASCII characters to begin with and it won't do anything in that case.
  • Don't validate strings you have already altered (here: after mysqli_real_escape_string).

  • You're already using mysqli, use prepared statements rather than tedious and error prone escaping.
  • Disallowing "special characters" in passwords makes them weaker, not stronger. Unless you have strong business reasons for this restriction (which legitimately may exist), don't limit the allowed character set.
  • Use more functions to make your code more readable.
  • Name your SQL columns, don't rely on the implicit order.
  • Use DEFAULT values in your database table definitions instead of passing default values through the query, if possible.
  • Check whether your SQL query succeeded (and/or use mysqli's exception error mode); presumably you have a UNIQUE constraint on the username, so the query may legitimately fail, and your code doesn't even know it.
function validateUsername($name) {
    return ctype_alnum($name) && strlen($name) >= 6;
}

function validatePassword($str) {
    return ctype_alnum($str) 
        && strlen($str) >= 8
        && preg_match('/[A-Z]/', $str)
        && preg_match('/[a-z]/', $str)
        && preg_match('/[0-9]/', $str);
}

function createUser(mysqli $db, $name, $password) {
    $stmt = $db->prepare('INSERT INTO `accounts` (`name`, `password`) VALUES (?, ?)');
    $stmt->bind_param('ss', $name, password_hash($password, PASSWORD_DEFAULT));
    return $stmt->execute();
}

if (!validateUsername($_POST['name'])) {
    echo 'Invalid name';
} else if (!validatePassword($_POST['password'])) {
    echo 'Invalid password';
} else if ($_POST['password'] !== $_POST['repeat_password']) {
    echo "Passwords don't match";
} else if (!createUser($db_link, $_POST['name'], $_POST['password'])) {
    echo 'Something went wrong';  // add better error handling here
} else {
    echo 'Account created';
}

Of course, you'll probably want to collect all the errors and output them next to the actual <input> elements when you inform the user about errors, instead of just failing on the first error that is produced. That's a bit too broad to tackle here though. And this could all be further improved with OOP or other larger architectural choices of course…

2 of 2
1

As an extension of deceze's excellent critique, I would like to advise that you validate the password with just one preg_match() call rather than five function calls including three separate preg_match() calls. The cost to this may mean reduced code comprehension depending on your understanding of regex, but it will yield more concise code and perform more efficiently.

function validatePassword($pass){
    // permitted characters throughout string ------------------------↓↓↓↓↓↓↓↓
    return preg_match('/^(?=[^A-Z]*[A-Z])(?=[^a-z]*[a-z])(?=[^\d]*\d)[a-zA-Z\d]{8,}$/',$pass)?true:false;
    // required characters----------↑↑↑-------------↑↑↑-----------↑↑            ↑-minimum length (no max)
}

Here is a PHP demo.

Regex Breakdown:

^                 # match from start of string
(?=[^A-Z]*[A-Z])  # lookahead for one uppercase letter (without advancing)
(?=[^a-z]*[a-z])  # lookahead for one lowercase letter (without advancing) 
(?=[^\d]*\d)      # lookahead for one digit (without advancing)
[a-zA-Z\d]{8,}    # only match if string is comprised of 8 or more of these characters
$                 # match until end of string

To relax the valid characters range, you might like to alter the character class just before $ to use .{8,} or specifically declare additional valid characters with [a-zA-Z\d!@#$%^&*()]{8,}.

🌐
WYSIWYG Web Builder
wysiwygwebbuilder.com › board index › wysiwyg web builder › forms › login tools questions
Password: changing preg_match in PHP Sign up - www.wysiwygwebbuilder.com
June 6, 2017 - B. Changing the whole preg_match for the password I see that the default preg_match for the password ^[A-Za-z0-9_!@$]{1,50}$ accepts only these special characters:_!@$ May I change the preg_match from this ^ A-Za-z0-9_!@$]{1,50}$ to this ^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,20} ?