2011-06-26 98 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類型,它也應該可以工作。