9

I'm a PHP beginner and currently learning the "Validating the File Upload" part.

I made a test.php page containing following code:

var_dump(@$_FILES['file']['type']);

First, I uploaded an image "img.gif" and it returned:

string 'image/gif' (length=9)

Then, I changed the image's extension to ".jpg" and it returned:

string 'image/jpeg' (length=10)

So I realized $_FILES["file"]["type"] only return the uploaded file extension but didn't actually check what file is it.

In this page, http://www.w3schools.com/php/php_file_upload.asp, there is a code:

$allowedExts = array("gif", "jpeg", "jpg", "png");
$extension = end(explode(".", $_FILES["file"]["name"]));
if ((($_FILES["file"]["type"] == "image/gif")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/jpg")
|| ($_FILES["file"]["type"] == "image/png"))
&& ($_FILES["file"]["size"] < 20000)
&& in_array($extension, $allowedExts))

I'm wondering why above codes check file extension twice? I deleted some from above codes and this is my new code:

$allowedExts = array("gif", "jpeg", "jpg", "png");
$extension = end(explode(".", $_FILES["file"]["name"]));
if (($_FILES["file"]["size"] < 20000) && in_array($extension, $allowedExts))

Is my code correct? Or do you have any better ways to validate the upload file is a image?

Thanks!

Eathen Nutt
  • 1,433
  • 5
  • 19
  • 27
  • 1
    Why my post got downvote? I just wanna make sure $_FILES["file"]["type"] is not necessay in the w3schools's codes – Eathen Nutt Mar 24 '13 at 05:57

3 Answers3

19

You should pass the tmp_name of the file* to getimagesize, it will give you the size and type of the image (if it is an image). If the passed argument is a file but not an image it returns false, that will allow you to validate.

Edit: The only reliable method of image validation is to make a copy of it using GD or Imagick - getimagesize can be easily hacked.

*: I mean, the temporal file created after upload.

For example:

if ($_SERVER['REQUEST_METHOD'] === 'POST')
{
    $file = $_FILES['file']['tmp_name'];
    if (file_exists($file))
    {
        $imagesizedata = getimagesize($file);
        if ($imagesizedata === FALSE)
        {
            //not image
        }
        else
        {
            //image
            //use $imagesizedata to get extra info
        }
    }
    else
    {
        //not file
    }
}

This code uses file_exists just to be general. In case no file were uploaded you would get $_FILES['file']['size'] = 0, $_FILES['file']['tmp_name'] = '' and $_FILES['file']['error'] = 4. See also is_readable. For the error values see file upload errors explained at php.net.

Michał Maluga
  • 453
  • 3
  • 14
Theraot
  • 31,890
  • 5
  • 57
  • 86
  • thanks for mentioning getimagesize function – Eathen Nutt Mar 24 '13 at 06:03
  • 2
    Please note that it does check if it's an image, but that doesn't mean it isn't an image that has been tempered with, see: http://php.webtutor.pl/en/2011/05/13/php-code-injection-a-simple-virus-written-in-php-and-carried-in-a-jpeg-image/ I recommend recreating the image using functions such as imagecreatefromstring and converting it to e.g. png. – Arie 50 mins ago – Arie Feb 18 '15 at 16:10
  • 1
    From getimagesize() PHP docs: `Do not use getimagesize() to check that a given file is a valid image.` –  Dec 03 '18 at 14:49
4
$allowedExts = array("gif", "jpeg", "jpg", "png");
$extension = end(explode(".", $_FILES["file"]["name"]));
if ((($_FILES["file"]["type"] == "image/gif")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/jpg")
|| ($_FILES["file"]["type"] == "image/png"))
&& ($_FILES["file"]["size"] < 20000)
&& in_array($extension, $allowedExts))

This is checked twice because, the extension of the file and 'file type' can be differ, so someone can not upload executable file with .png extension.

In your modified code, it is possible to upload a different type of file with modified extension. like they can upload word document with '.png' extension.

Your new code is just checking extension and don't have double check.

Maulik Vora
  • 2,544
  • 5
  • 28
  • 48
0

Your new code only checks the extension of the file and the size of the file. It doesn't checks the type of the file.

I will strongly recommend to use your old code because there it checks the file type also.

Yogesh Suthar
  • 30,424
  • 18
  • 72
  • 100
  • 1
    var_dump(@$_FILES['file']['type']) returned string 'image/png' (length=9) for an exe file but with a png extension, so it didn't check the file type but only the extension. – Eathen Nutt Mar 24 '13 at 05:52