2011-06-26 65 views
24

我想检查一个上传的文件是否是一个图像文件(如PNG,JPG,JPEG,GIF,BMP)或其他文件。问题是我正在使用Uploadify上传文件,这些文件会更改MIME类型,并给出“文本/八进制”或其他类型的MIME类型,无论您上传的是哪种文件类型。如何检查上传的文件是否为没有MIME类型的图像?

有没有办法检查上传的文件是否是除了使用PHP检查文件扩展名之外的图像?

回答

32

您可以使用getimagesize(),它在非图像上返回零大小。

+2

据我所知,这实际上是完成这件事的可接受的手段。如果它们存在,我会对更好的方法有兴趣。 –

+0

文档说它返回一个包含7个元素的数组,我需要检查它的图像是否需要检查哪个元素? –

+0

零和一个宽度和高度。 –

6

您可以通过检查文件开头的幻数来验证图像类型。

例如:每个JPEG文件以“FF D8 FF E0”块开始。

下面是使用exif_imagetype检索实际类型的图像magic numbers

+6

不安全IMO,攻击者可以“FF D8 FF E0”第一个字节,然后添加可能会被执行 – Reacen

+2

Reacen一些PHP代码,没有什么是安全的,PHP代码也可以通过使用JPG元数据部分将其注入到有效的JPG文件中。 – rcode

3

尝试更多的信息。如果文件太小就会抛出错误,如果找不到则返回错误

37

我对这个主题的想法很简单:所有上传的图片都是邪恶的。

而且不仅因为它们可以包含恶意代码,而且特别是因为元标记。我了解使用隐藏元标记浏览网页以找到一些受保护图像的抓取工具,然后播放其版权。也许有点偏执狂,但由于用户上传的图片在版权问题上失控,我谨慎考虑。

为了摆脱这些问题,我使用gd系统地将所有上传的图像转换为png。这有很多优点:从最终的恶意代码和元标记,图像是干净的,我只有一个格式的所有上传的图像,我可以调整图像大小,以符合我的标准,并... 我立即知道是否图像是否有效!如果图像无法打开进行转换(使用imagecreatefromstring,它不关心图像格式),那么我认为图像无效。

一个简单的实现看起来是这样的:

function imageUploaded($source, $target) 
{ 
    // check for image size (see @DaveRandom's comment) 
    $size = getimagesize($source); 
    if ($size === false) { 
     throw new Exception("{$source}: Invalid image."); 
    } 
    if ($size[0] > 2000 || $size[1] > 2000) { 
     throw new Exception("{$source}: Too large."); 
    } 

    // loads it and convert it to png 
    $sourceImg = @imagecreatefromstring(@file_get_contents($source)); 
    if ($sourceImg === false) { 
     throw new Exception("{$source}: Invalid image."); 
    } 
    $width = imagesx($sourceImg); 
    $height = imagesy($sourceImg); 
    $targetImg = imagecreatetruecolor($width, $height); 
    imagecopy($targetImg, $sourceImg, 0, 0, 0, 0, $width, $height); 
    imagedestroy($sourceImg); 
    imagepng($targetImg, $target); 
    imagedestroy($targetImg); 
} 

为了测试它:

header('Content-type: image/png'); 
imageUploaded('http://www.dogsdata.com/wp-content/uploads/2012/03/Companion-Yellow-dog.jpg', 'php://output'); 

这并不完全回答你的问题,因为这是同一种黑客比接受的答案的,但我给你我的理由使用它,至少:-)

+0

我完全同意@Alain Tiemblo“我使用gd系统地将所有上传的图片转换为png”。这是安全性的一种方式。 – PauloASilva

+0

事实上,我的一位同事指出,如果你允许动画GIF,这是行不通的。真正。 –

+0

我同意你但是...每个人都知道GIF可以被利用([示例])(http://www.phpclasses.org/blog/post/67-PHP-security-exploit-with-GIF-images.html ))。如果您需要支持GIF,也许您必须投入一些时间和精力来过滤/清除格式。 – PauloASilva

5

如果Uploadify真的改变了MIME类型 - 我会认为它是一个错误。 这是没有意义的,因为开发商块从PHP与基于 MIME类型功能的工作:

这是一个小辅助函数,它返回基于文件的前6个字节的mime类型。

/** 
* Returns the image mime-type based on the first 6 bytes of a file 
* It defaults to "application/octet-stream". 
* It returns false, if problem with file or empty file. 
* 
* @param string $file 
* @return string Mime-Type 
*/ 
function isImage($file) 
{ 
    $fh = fopen($file,'rb'); 
    if ($fh) { 
     $bytes = fread($fh, 6); // read 6 bytes 
     fclose($fh);   // close file 

     if ($bytes === false) { // bytes there? 
      return false; 
     } 

     // ok, bytes there, lets compare.... 

     if (substr($bytes,0,3) == "\xff\xd8\xff") { 
      return 'image/jpeg'; 
     } 
     if ($bytes == "\x89PNG\x0d\x0a") { 
      return 'image/png'; 
     } 
     if ($bytes == "GIF87a" or $bytes == "GIF89a") { 
      return 'image/gif'; 
     } 

     return 'application/octet-stream'; 
    } 
    return false; 
} 
2

是不是可以用finfo_file询问文件?

$finfo = finfo_open(FILEINFO_MIME_TYPE); 
$mimetype = finfo_file($finfo, $filename); //should contain mime-type 
finfo_close($finfo); 

这个答案是未经测试,而是基于this forum discussion的Uploadify论坛。

我还要指出,finfo应该是"try to guess the content type and encoding of a file by looking for certain magic byte sequences at specific positions within the file",所以在我看来,即使Uploadify指定了错误的MIME类型,它也应该可以工作。