检查上传文件最可靠的方法是图像

时间:2014-12-23 05:52:03

标签: php image file-upload mime-types file-type

我想验证我的上传文件是图像与否。在搜索之后,我找到了两种方式,我认为这是一种很好的方法。第一个代码是:

$whitelist_type = array('image/jpeg', 'image/png','image/gif');
$fileinfo = finfo_open(FILEINFO_MIME_TYPE);

if (!in_array(finfo_file($fileinfo, $file['tmp_name']), $whitelist_type)) {
$error[]  = "Uploaded file is not a valid image";
}

和第二个代码:

if (!getimagesize($_FILES['photo']['tmp_name'])) {
$error[]  = "Uploaded file is not a valid image";
}

哪个代码更可靠来检查它是一个图像,为什么?还是比这更好的方式?感谢。

4 个答案:

答案 0 :(得分:4)

finfo_*库会很好,但它适用于> = 5.3.0个版本,

AND getimagesize() GD库函数,即返回图片信息WxHsize

如果图片无效,则getimagesize()显示警告,以便更好地使用finfo_*函数验证图片,

您也可以使用跨版本代码,请参阅下面的示例代码

<?php 
$file = $_FILES['photo'];
$whitelist_type = array('image/jpeg', 'image/png','image/gif');
$error = null;
if(function_exists('finfo_open')){    //(PHP >= 5.3.0, PECL fileinfo >= 0.1.0)
   $fileinfo = finfo_open(FILEINFO_MIME_TYPE);

    if (!in_array(finfo_file($fileinfo, $file['tmp_name']), $whitelist_type)) {
      $error[]  = "Uploaded file is not a valid image";
    }
}else if(function_exists('mime_content_type')){  //supported (PHP 4 >= 4.3.0, PHP 5)
    if (!in_array(mime_content_type($file['tmp_name']), $whitelist_type)) {
      $error[]  = "Uploaded file is not a valid image";
    }
}else{
   if (!@getimagesize($file['tmp_name'])) {  //@ - for hide warning when image not valid
      $error[]  = "Uploaded file is not a valid image";
   }
}

答案 1 :(得分:2)

为什么不使用exif_imagetype

if (exif_imagetype($file['tmp_name']) != (IMAGETYPE_JPEG || IMAGETYPE_GIF || IMAGETYPE_PNG)) {
    $error[]  = "Uploaded file is not a valid image";
}

它可能会比其他任何一个都快。 (PHP 4&gt; = 4.3.0,PHP 5)

答案 2 :(得分:2)

从安全角度来看,您可能最好将上传的文件推测转换为图片,看看是否成功,并保留并提供转换后的结果在那里。

您可以根据您检测到的MIME类型,使用GD library中的imagecreatefrom...()个功能之一,例如来自$_FILES数组,和/或来自exif_imagetype()finfo_file()等。

问题在于,有些漏洞假装是有效图像(在某些情况下有效图像),但也是有效的JavaScript,Flash或其他可能运行的代码容器在某些情况下由客户的浏览器。

参见例如https://www.defcon.org/images/defcon-15/dc15-presentations/dc-15-schrenk.pdf

答案 3 :(得分:0)

我使用的最快方法是自定义PHP函数,该函数从文件读取特定字节。当检查文件很大(电影,iso图像等)时,它的工作速度比getimagesize快得多。

fastImageGet('image.jpg');         // returns size and image type in array or false if not image
fastImageGet('image.jpg', 'type'); // returns image type only
fastImageGet('image.jpg', 'size'); // returns image size only

function fastImageGet($file, $what=null) {

    if (!in_array($what, ['size', 'type']))
        $what = null;

    // INIT

    $pos = 0; $str = null;

    if (is_resource($file))
        $fp = $file;

    elseif (!@filesize($file))
        return false;

    else
        try {
            $fp = fopen($file, 'r', false);
        } catch (\Exception $e) {
            return false;
        }


    // HELPER FUNCTIONS

    $getChars = function($n) use (&$fp, &$pos, &$str) {
        $response = null;

        if (($pos + $n - 1) >= strlen($str)) {
            $end = $pos + $n;

            while ((strlen($str) < $end) && ($response !== false)) {
                $need = $end - ftell($fp);

                if (false !== ($response = fread($fp, $need)))
                    $str .= $response;
                else
                    return false;
            }
        }

        $result = substr($str, $pos, $n);
        $pos += $n;
        return $result;
    };

    $getByte = function() use ($getChars) {
        $c = $getChars(1);
        $b = unpack('C', $c);
        return reset($b);
    };

    $readInt = function ($str) {
        $size = unpack('C*', $str);
        return ($size[1] << 8) + $size[2];
    };


    // GET TYPE

    $t2 = $getChars(2);

    if ($t2 === 'BM')
        $type = 'bmp';
    elseif ($t2 === 'GI')
        $type = 'gif';
    elseif ($t2 === chr(0xFF) . chr(0xd8))
        $type = 'jpeg';
    elseif ($t2 === chr(0x89) . 'P')
        $type = 'png';
    else
        $type = false;

    if (($type === false) || ($what === 'type')) {
        fclose($fp);
        return $type;
    }


    // GET SIZE

    $pos = 0;

    if ($type === 'bmp') {
        $chars = $getChars(29);
        $chars = substr($chars, 14, 14);
        $ctype = unpack('C', $chars);
        $size = (reset($ctype) == 40)
            ? unpack('L*', substr($chars, 4))
            : unpack('L*', substr($chars, 4, 8));

    } elseif ($type === 'gif') {
        $chars = $getChars(11);
        $size = unpack('S*', substr($chars, 6, 4));

    } elseif ($type === 'jpeg') {
        $state = null;

        while (true) {

            switch ($state) {

                default:
                    $getChars(2);
                    $state = 'started';
                    break;

                case 'started':
                    $b = $getByte();
                    if ($b === false) {
                        $size = false;
                        break 2;
                    }
                    $state = $b == 0xFF ? 'sof' : 'started';
                    break;

                case 'sof':
                    $b = $getByte();

                    if (in_array($b, range(0xE0, 0xEF)))
                        $state = 'skipframe';

                    elseif (in_array($b, array_merge(range(0xC0, 0xC3), range(0xC5, 0xC7), range(0xC9, 0xCB), range(0xCD, 0xCF))))
                        $state = 'readsize';

                    elseif ($b == 0xFF)
                        $state = 'sof';

                    else
                        $state = 'skipframe';

                    break;

                case 'skipframe':
                    $skip = $readInt($getChars(2)) - 2;
                    $state = 'doskip';
                    break;

                case 'doskip':
                    $getChars($skip);
                    $state = 'started';
                    break;

                case 'readsize':
                    $c = $getChars(7);
                    $size = [$readInt(substr($c, 5, 2)), $readInt(substr($c, 3, 2))];
                    break 2;
            }
        }

    } elseif ($type === 'png') {
        $chars = $getChars(25);
        $size = unpack('N*', substr($chars, 16, 8));
    }


    // COMPLETE

    fclose($fp);

    if (is_array($size))
        $size = array_values($size);

    return ($what === 'size') ? $size : [$type, $size];
}