I have a PHP website where registered users can upload an avatar. One of the restrictions is that people can only upload either a .jpg or .jpeg file with only alphanumeric characters, anything else is rejected. This is to make sure I only get uploads like "avatar.jpg", and not "evilcode.php" or "secretcode.php.jpg". I'm also planning other checks, but right now I can't get this first step to work.
I am using this regex expression:
[a-zA-Z0-9]{1,150}+\.+(jpe?g)
This is the code I'm currently using. The function is called from another php file, with $_FILES['avatar'] as a parameter.
public function updateAvatar($avatar)
{
$regex = '^[a-zA-Z0-9]{1,100}+\.+(jpe?g)$';
$name = $avatar['name'];
$result = preg_match_all($regex, $name);
if($result === 1)
{
return true;
} else
{
return false;
}
}
This always returns false, when uploading either "avatar.jpg", "code.php", or "duck.gif". According to the PHP manual, this code should be correct. The method returns either an integer or a boolean, and warns that you should use ===, not == to compare the result. Does anyone know what I did wrong?
When you check yours incoming file through regex, it is possible to upload the exploit file with executable mime, so you must check mime of the file for safe uploading:
public function updateAvatar($avatar)
{
$available_mimes = array(
'jpeg' => 'image/jpeg',
'jpg' => 'image/jpg',
'iejpg' => 'image/pjpeg' // this mime sent Internet Explorer for jpg files
);
if(in_array($avatar['type'], $available_mimes)
{
return true;
}
else
{
return false;
}
}
As for your regex function:
public function updateAvatar($avatar)
{
// Use regex with ignore case flag (for validating jpg and JPG)
// Uncomment next line, if you want to check files with "_" symbols at name
// if(preg_match('/^(\w+)\.(jpe?g)$/i', $avatar['name']))
if(preg_match('/^([a-z0-9]+)\.(jpe?g)$/i', $avatar['name']))
{
return true;
}
else
{
return false;
}
}
And try to use preg_match()
Add delimiters to your regex expression, try this:
$regex = '/^[a-zA-Z0-9]{1,100}+\.+(jpe?g)$/';
// ^ ^
I see two issues. First, PHP's RegEx functions expect patterns to be surrounded with delimiter characters of your choice (regex options go after the end delimiter).
For example: $regex = '/^[a-zA-Z0-9]{1,100}+\.+(jpe?g)$/';
or $regex = '|^[a-zA-Z0-9]{1,100}+\.+(jpe?g)$|';
Second, you use both {1,100}
("there are one to 100 characers of the specified class") as well as +
("there is one or more charecter of the specified class"). Use only one of those.
try using this,
/\.(jpg|jpeg|png|gif)(?:[\?\#].*)?$/i
Your regex doesn't have delimiters:
/^[a-zA-Z0-9]{1,100}+\.+(jpe?g)$/
and you don't need preg_match_all()
as you check a single avatar file name:
public function updateAvatar($avatar)
{
$regex = '/^[a-zA-Z0-9]{1,100}+\.+(jpe?g)$/';
$name = $avatar['name'];
if(preg_match($regex, $name))
return true;
return false;
}
However you'd better change your regex to this:
/^[a-zA-Z0-9]{1,100}\.(jpe?g)$/
a +
sign after quantifiers is not needed, also you shouldn't validate multiple dot
s at the end of name as your requirements.