nulled
nulled

Reputation: 423

Using preg_match to detect special characters not working

I'm trying to stop users from being able to put any characters in the username box apart from a-z(A-Z), 0-9 and spaces. Here's the HTML to start off with:

 <form action='register.php' method='post'>
    <div class="field-wrap">
        <label>
            Username<span class="req">*</span>
        </label>
        <input type="text" name="username" required autocomplete="nope" />
    </div>

    <div class="field-wrap">
        <label>
            Email Address<span class="req">*</span>
        </label>
        <input type="email" name="email" required autocomplete="nope" />
    </div>

    <div class="field-wrap">
        <label>
            Set A Password<span class="req">*</span>
        </label>
        <input type="password" name="password" required autocomplete="nope" />
    </div>

    <button type="submit" class="button button-block" />
    REGISTER</button>

</form>

Pretty self explanatory, right?

Here's the PHP on register.php:

$username = $_POST['username'];
$password = $_POST['password'];

if(preg_match('/[^a-zA-Z0-9[:space:]]+$/', $username)){
    //pass
}
else{
$message = "Your username may only contain letters, numbers and spaces";
$_SESSION['error'] = $message;
header("Location:auth.php");
}

// do all the other stuff like add user to database etc
header("Location:index.php");

When I try to create a user with a username such as "test#@!?*^'/"()", the preg_match function doesn't work. Instead of redirecting back to the login/register page (auth.php), it adds the user to the database and redirects me to the homepage (index.php).

I have also tried /^[a-z0-9 .-]+$/i for the parameters in preg_match but that doesn't work either.

Just a side note, I'm not using this for security reasons, I use stripslashes and mysql_real_escape_string AFTER the preg_match.

Any ideas, or a better way to only allow a-z(A-Z), 0-9 and spaces? I have been trying to solve this for hours now and to no avail. Thanks!

Upvotes: 1

Views: 3795

Answers (5)

Tinel Barb
Tinel Barb

Reputation: 141

This is a good example for a short security tutorial.

The original code presented by OP allows access if $username does not contains characters from list, at least one of them:

if(preg_match('/[^a-zA-Z0-9[:space:]]+$/', $username)){
    //pass
}

The updated code posted here is doing the job:

if(preg_match("/^[A-Za-z0-9 ]+?$/", $username)){
    //pass
    $passed = 1;
}

However, correctly is to refuse access if $username contains ANY characters outside from the allowed set:

if(!preg_match("/[^A-Za-z0-9 ]/", $username)){
    //allows access
    $passed = 1;
} else { 
    //refuse access
    $passed = 0;
}

This will cover and refuse anything outside from the allowed character set. The caret sign "^", usually is a metacharacter that assert start of subject (or line, in multiline mode), like in /^(A sentence).*$/, but when used in a character class, like [^abc] it means "NOT the characters inside the brackets" (reference).

Upvotes: 0

nulled
nulled

Reputation: 423

Solved this now thanks to Ghulam... his logic was great although the code he wrote was wrong so I've updated it.

Also updated my answer with fusion3k's die(); approach just to make sure the code is completely finished.

$username = $_POST['username'];
$password = $_POST['password'];
$passed = 0;

if(preg_match("/^[A-Za-z0-9 ]+?$/", $username)){
    //pass
    $passed = 1;
}
if($passed == 0){
    $message = "Your username may only contain letters, numbers and spaces";
    $_SESSION['error'] = $message;
    header("Location:auth.php");
    die();
}
if($passed == 1){
//add user to database
header("Location:index.php");
}

We set $passed as 0 to begin with.

If $username only contains letters a-z(A-Z), 0-9 and spaces then we set $passed to 1 as it has passed the preg_match check.

If $username contains any other characters apart from these, (@, %, ^ etc) then we leave the $passed variable as 0.

If $passed is 0 then the username is invalid, so return the user to the register/login page (auth.php) and give them an error message.

If $passed is 1 then the username is valid so we can add the user to the database and return them to the homepage.

die(); is used to make sure the code stops reading/running after the header redirect has been sent. The page might redirect but the user could still be added to the database!

Upvotes: 0

fusion3k
fusion3k

Reputation: 11689

About your original question:

This regular expression doesn't work properly due to caret (^) position:

/[^a-zA-Z0-9[:space:]]+$/
  ↑

In this position, caret negate following pattern inside square brackets. In fact, your pattern search for any not a-zA-Z0-9....

To match a string with only alphanumeric characters and spaces you have to move the caret at start of pattern. In this position the caret means “start of string”:

/^[a-zA-Z0-9[:space:]]+$/
 ↑

But you can also simplify your pattern, and replace [:space:] with a real blank space ([:space:] and \s match also newline, tab, etc...1). Try this regular expression:

/^[A-z0-9 ]+$/

Your script still not working:

The solution is die().

If the string doesn't match the pattern, you execute this code:

$message = "Your username may only contain letters, numbers and spaces";
$_SESSION['error'] = $message;
header("Location:auth.php");

Sending headers doesn't interrupt the script, so the remaining code is executed and the last sent header (Location:index.php) is loaded.

Force script termination after sending header:

header("Location:auth.php");
die();

1 From PHP documentation: “The "whitespace" characters are HT (9), LF (10), FF (12), CR (13), and space (32). However, if locale-specific matching is happening, characters with code points in the range 128-255 may also be considered as whitespace characters, for instance, NBSP (A0).”

Upvotes: 1

Ghulam Ali
Ghulam Ali

Reputation: 1935

Use this preg_match code to only allow Letters (including uppercase), Numbers, and Spaces:

$Passed = 0;
$username = $_POST['username'];
$password = $_POST['password'];

if(!preg_match("/[^a-z0-9 ]/i", $username)){
    $Passed = 1;
    //stop header location here.
}
else{
    $message = "Your username may only contain letters, numbers and spaces";
    $_SESSION['error'] = $message;
    header("Location:auth.php");
}
if ($Passed == 0){
    header("Location:index.php");
}

Upvotes: 1

Phillip Bartschinski
Phillip Bartschinski

Reputation: 643

Change your regex to:

/^[\d\w\s]+?$/

You can easy test with http://regexr.com/

Upvotes: 0

Related Questions