Reputation: 423
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
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
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
Reputation: 11689
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 ]+$/
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
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
Reputation: 643
Change your regex to:
/^[\d\w\s]+?$/
You can easy test with http://regexr.com/
Upvotes: 0