編碼的世界 / 優質文選 / 文明

php上傳文件文件類型的判斷方法


2021年9月25日
-   

根據擴展名判斷類型的弊端
        正如我剛開始接觸 php 一樣,我們許多人在使用 php 進行文件的上傳和存儲時,都會給文件進行重名命並保存到可寫文件夾下,然後我們在其中一個失誤的地方便是采用上傳文件的擴展名作為判斷文件類型的依據。這樣做其實與後門大開無異,舉一個簡單的例子,通過擴展名判斷一般是字符串的截取判斷,或者是使用$_FILE數組判斷,然後如果用戶上傳的文件名為 image.php.png, image.png.php, image.php%****.png 之類的話,判斷就會出錯。另外,$_FILES['type']可能被篡改。
       夜已深,話不多言。送上我判斷文件名的方法:讀取文件頭四個字節作為判斷。
        下面直接上代碼,相信略有些php功底的朋友,讀來都不成問題。我實現的是僅支持word和pdf文件,且文件大小小於512kb:
	$tmpname = $_FILES ['userfile'] ['tmp_name'];
if(is_uploaded_file($tmpname)) {
$mimetype = detectMIME($tmpname);
$tuozhanming = getFileExt($filename, $mimetype);
if($tuozhanming == "type_error"){
echo '僅支持word和pdf文件,且文件大小小於512kb:<a href='.$reurl.'>請重試</a>';
exit();
}
}else{
$_FILES ['userfile'] ['error'] = 6;
}
if ($_FILES ['userfile'] ['error'] > 0) {
echo 'Problem: ';
switch ($_FILES ['userfile'] ['error']) {
case 1 :
echo '上傳文件過大:<a href='.$reurl.'>請重試</a>';
break;
case 2 :
echo '上傳文件過大:<a href='.$reurl.'>請重試</a>';
break;
case 3 :
echo '文件上傳丟失:<a href='.$reurl.'>請重試</a>';
break;
case 4 :
echo '無文件被上傳:<a href='.$reurl.'>請重試</a>';
break;
case 6 :
echo '僅支持word和pdf文件,且文件大小小於512kb:<a href='.$reurl.'>請重試</a>';
break;
case 7 :
echo '上傳文件存儲失敗:<a href='.$reurl.'>請重試</a>';
break;
}
exit ();
}
//判斷文件類型
//上傳文件
$_FILES ['userfile'] ['name'] = time () . "." . $tuozhanming;
$upfile = '../uploads/' . $_FILES ['userfile'] ['name'];
if ( !move_uploaded_file ( $_FILES ['userfile'] ['tmp_name'], $upfile )) {
echo 'Problem: 文件移動失敗';
exit ();
}

}
function detectMIME($filename) {
$file = fopen ( $filename, "rb" );
$finfo = finfo_open ( FILEINFO_MIME );
if (! $finfo) {
// 直接讀取文件的前4個字節,根據硬編碼判斷
$file = fopen ( $filename, "rb" );
$bin = fread ( $file, 4 ); //只讀文件頭4字節
fclose ( $file );
$strInfo = @unpack ( "C4chars", $bin );
//dechex() 函數把十進制轉換為十六進制。
$typeCode = dechex ( $strInfo ['chars1'] ) .
dechex ( $strInfo ['chars2'] ) .
dechex ( $strInfo ['chars3'] ) .
dechex ( $strInfo ['chars4'] );
$type = '';
switch ($typeCode) //硬編碼值查表
{
case "504b34" :
$type = 'application/zip; charset=binary';
break;
case "d0cf11e0" :
$type = 'application/vnd.ms-office; charset=binary';
break;
case "25504446" :
$type = 'application/pdf; charset=binary';
break;
default :
$type = 'application/vnd.ms-office; charset=binary';
break;
}
} else {
//finfo_file return information of a file
$type = finfo_file ( $finfo, $filename );
}
return $type;
function getFileExt($filename, $type) {
switch ($type) {
case "application/zip; charset=binary" :
$extType = "docx";
break;
case "application/vnd.ms-office; charset=binary" :
$extType = "doc";
break;
case "application/msword; charset=binary" :
$extType = "doc";
break;
case "application/pdf; charset=binary" :
$extType = "pdf";
break;
default :
$extType = "type_error";
break;
}
return $extType;
}

 文件類型對照表地址
        關於文件類型硬編碼值的對照表可查此處:http://www.garykessler.net/library/file_sigs.html
結束語
        對於php編程而言,在下也是個半吊子,上述方法還有的缺陷和漏洞,還忘指證。有討論批評才會有進步。另外,不想複制的朋友,我已上傳寫好的php文件,下載即可使用:
http://download.csdn.net/detail/agangdi/4911949

熱門文章