根据扩展名判断类型的弊端
正如我刚开始接触 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