1

Working on a little upload script here. I'm trying to check if the uploaded image really is an image and not just a renamed PHP file.

When the script is posted I can print the array with

foreach ($_FILES['images']['name'] as $key => $value){             
        print_r(getimagesize($_FILES['images']['tmp_name'][$key]));

That works just fine, so it won't return false. But even if I upload a file that is not an image, it won't give false. It just returns nothing at all, and the rest of my script just processes the thing like an image.

Could anyone tell me what I am doing wrong?

Peter O.
  • 32,158
  • 14
  • 82
  • 96
user1362916
  • 119
  • 2
  • 14

5 Answers5

4

Upload

you can not use getimagesize on $_FILES['images']['tmp_name'][$key] directly .. you need to copy it into your system first before you can use it

Use $_FILES['images']['size'][$key] temporarily

Or

  move_uploaded_file($_FILES['images']['tmp_name'][$key], $destination);
  print_r(getimagesize($destination));

Fake Image

Please not that $_FILES['images']['type'][$key] can be faked

Using Fake image Headers

Example

file_put_contents("fake.png", base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAABGdBTUEAALGPC/xhBQAAAAZQTFRF////
AAAAVcLTfgAAAAF0Uk5TAEDm2GYAAAABYktHRACIBR1IAAAACXBIWXMAAAsSAAALEgHS3X78AAAAB3RJTUUH0gQCEx05cq
KA8gAAAApJREFUeJxjYAAAAAIAAUivpHEAAAAASUVORK5CYII='));

Uploading fake.png

array
  'name' => 
    array
      0 => string 'fake.png' (length=8)
  'type' => 
    array
      0 => string 'image/png' (length=9)
  'tmp_name' => 
    array
      0 => string 'C:\Apache\xampp\tmp\php44F.tmp' (length=30)
  'error' => 
    array
      0 => int 0
  'size' => 
    array
      0 => int 167

Validate Image

Usage

var_dump ( getimagesizeReal ( "fake.png" ) );

Function Used

function getimagesizeReal($image) {

    $imageTypes = array (
            IMAGETYPE_GIF,
            IMAGETYPE_JPEG,
            IMAGETYPE_PNG,
            IMAGETYPE_SWF,
            IMAGETYPE_PSD,
            IMAGETYPE_BMP,
            IMAGETYPE_TIFF_II,
            IMAGETYPE_TIFF_MM,
            IMAGETYPE_JPC,
            IMAGETYPE_JP2,
            IMAGETYPE_JPX,
            IMAGETYPE_JB2,
            IMAGETYPE_SWC,
            IMAGETYPE_IFF,
            IMAGETYPE_WBMP,
            IMAGETYPE_XBM,
            IMAGETYPE_ICO 
    );
    $info = getimagesize ( $image );
    $width = @$info [0];
    $height = @$info [1];
    $type = @$info [2];
    $attr = @$info [3];
    $bits = @$info ['bits'];
    $channels = @$info ['channels'];
    $mime = @$info ['mime'];

    if (! in_array ( $type, $imageTypes )) {
        return false; // Invalid Image Type ;
    }
    if ($width <= 1 && $height <= 1) {
        return false; // Invalid Image Size ;
    }

    if($bits === 1)
    {
        return false; // One Bit Image .. You don't want that  ;
    }
    return $info ;
}
Baba
  • 94,024
  • 28
  • 166
  • 217
  • 1
    Yes but i do not want to move the uploaded file unless i am 100% sure it is an image – user1362916 May 05 '12 at 18:55
  • you put it in a temporarily directory first .. if its not a valid image use `unlink` to remove it – Baba May 05 '12 at 18:56
  • Aah i see what you mean! Didn't think of that. Ill try it right away. – user1362916 May 05 '12 at 18:58
  • 1
    Or you can use $_FILES['images']['type'][$key] to check the MIME type to make sure it's a image/png, image/gif ... etc etc before you uploaded it. – Jamie Bicknell May 05 '12 at 19:02
  • That can easy be tricked ... `getimagesize` is the best option have seen so far @Jamie Bicknell – Baba May 05 '12 at 19:04
  • @Baba I agree it can be tricked by changing the file extension, whilst getimagesize() will return the true MIME. However in terms of primliminary checkes, it's worth doing $_FILES['images']['type'] anyway and then once it's uploaded, check the getimagesize() too. – Jamie Bicknell May 05 '12 at 19:12
  • @Baba Also, you can use getimagesize() on $_FILES['images']['tmp_name'] directly, but using it on a non-image file will not return anything – Jamie Bicknell May 05 '12 at 19:15
  • you can also fake image content header not just the extension ` ‰PNG IHDR%ÛVÊgAMA±üaPLTEÿÿÿUÂÓ~tRNS@æØfbKGDˆH pHYsÒÝ~ütIMEÒ9r¢€ò IDATxœc`H¯¤qIEND®B`‚` that is fake image – Baba May 05 '12 at 19:17
  • Renaming or changing file extension does not change the MIME type. I haven't tried it but it should not happen because it's not the way MIME type can be modified or changed. – SachinGutte May 05 '12 at 19:18
  • I am already checking on [type] , but i also wanted to make sure noone could upload renamed files. I tried moving it first like this : move_uploaded_file($_FILES['images']['tmp_name'][$key], $tmpmap.$value); print_r(getimagesize($tmpmap.$value)); , but it still returns nothing at all.. the image does get uploaded – user1362916 May 05 '12 at 19:19
  • See updated answer for a fake image that would show valid `png` mime type – Baba May 05 '12 at 19:21
  • @Baba With your fake image, even getimagesize() returns it as a image/png. For all intensive purposes, it IS a PNG as on a server, it will be run as a PNG. – Jamie Bicknell May 05 '12 at 19:28
  • @Baba : yes it alters the MIME type but then it becomes unreadable image file with some garbage( even if php code is written in it ) data. It'll ultimately become an image. From server it can be only accessible as image only. No one can reverse it and run it on same server. JamieBicknell: Yes, it'll become image and that can not reveresed to get some data or code out of it that can cause harm. Even if someone follow this process, he can not do much other than being able to upload fake image with garbage in it. – SachinGutte May 05 '12 at 19:34
  • I would update my code with a proper function to show you how it is done ... 10mins – Baba May 05 '12 at 19:37
  • @phazorRise Exactly, if the server thinks it's a PNG it will run as PNG hence no code being run. – Jamie Bicknell May 05 '12 at 19:45
  • there is a lot of ways to attack / spam a site with fake image ... its just putting PHP into an image .. i believe that would be for another topic `Hacking Site with Image` :D – Baba May 05 '12 at 19:47
  • @JamieBicknell. How intensive are your purposes? – TRiG Apr 27 '17 at 14:38
1

Firstly, you can use getimagesize on $_FILES['images']['tmp_name'], so that's not an issue.

If you want to check if the file is an image, then try this:

if(isset($_POST['submit'])) {
    $check = getimagesize($_FILES['images']['tmp_name']);
    if($check !== false) {
        echo 'File is an image - ' . $check['mime'];
    }
    else {
        echo 'File is not an image';
    }
}
Jamie Bicknell
  • 2,306
  • 17
  • 35
  • I used exactly your code (in a foreach loop), but it ALWAYS returnse file is not an image, no matter if its a valid one or not =( – user1362916 May 05 '12 at 19:31
  • @user1362916 Did you copy my code exactly, as I have a type and using $_FILES['image'] instead of your $_FILES['images']. I shall update the snippet now – Jamie Bicknell May 05 '12 at 19:42
1

I'd like to recommend against trusting the results of getimagesize() when deciding whether to place the uploaded file anywhere in your document root. That's because PHP code embedded in GIF files (titled like image.gif.php) will be identified as images by getimagesize(), but serving them will run the PHP code inside them in addition to displaying the image. Here is some more information on the issue.

The article linked above recommends setting up a separate controller through which all the user uploaded files are served. Files read with readfile() are not parsed when accessed through the local filesystem.

Kaivosukeltaja
  • 15,541
  • 4
  • 40
  • 70
0

You can simply use $_FILES['images']['type'] . it'll give you the type of file uploaded. Then check it againt octate stream or other executable file. If so then do not allow it.

SachinGutte
  • 6,947
  • 5
  • 35
  • 58
0

@user1362916 If you are uploading multiple images with HTML then perhaps you need to add one more array like this.

if(isset($_POST['submit'])) {
    $check = getimagesize($_FILES['images']['tmp_name'][$i]);
    if($check !== false) {
        echo 'File is an image - ' . $check['mime'];
    }
    else {
        echo 'File is not an image';
    }
}

Here check [i] because this is for multiple file upload.

Below is full script

<!DOCTYPE html>
<html>
<body>

<form action="#" method="post" enctype="multipart/form-data">
    Select image to upload:
    <input name="my_files[]" type="file" multiple="multiple" />
    <input type="submit" value="Upload Image" name="submit">
</form>


<?php

 if (isset($_FILES['my_files']))
 {
    $myFile = $_FILES['my_files'];
    $fileCount = count($myFile["name"]);


        for ($i = 0; $i <$fileCount; $i++)
         {
           $error = $myFile["error"][$i]; 

            if ($error == '4')  // error 4 is for "no file selected"
             {
               echo "no file selected";
             }
            else
             {
               if(isset($_POST['submit'])) {
                 $check = getimagesize($_FILES['my_files']['tmp_name'][$i]);
                 if($check !== false) {
                 echo 'File is an image - ' . $check['mime'];
                 }
                 else {
                 echo 'File is not an image';
                   }
                 }

             }
       }  
 }
        ?>


</body>
</html>
shiv
  • 192
  • 3
  • 13