PHP 使用硬编码检测文件 MIME
很多系统重需要使用到文件上传功能,如果有人故意将文件的后缀名改成符合要求的文件,比如.exe改成.jpg文件,这样可以上传文件,但是却有别攻击的风险。我们可以根据文件的硬编码来检测文件的MIME类型,这样文件的类别就不会出错。
主要思路是读取文件头的钱4个字节,参考文件硬编码,进行匹对:
<?php
namespace App\Services\Tools;
use App\Services\BaseService;
class FileService extends BaseService
{
public function fileCheck($file)
{
$mime_type = null;
$file_ext = null;
$origin_ext = null;
if(is_uploaded_file($file)) {
$mime_type = $this->getFileMIME($file);
$file_ext = $this->getFileExt($mime_type);
$ext = $file->getClientOriginalExtension();// 获取文件的扩展名
if($file_ext == "type_error"){
throw new Exception('文件类型不支持,请重试');
}
$origin_ext = $this->fileExtCheck($file_ext, $ext); // 对文件扩展名验证
}else{
$_FILES ['temp'] ['error'] = 6;
}
$this->fileUploadCheck($_FILES); // 文件上传验证
if(!empty($mime_type)){
return ["mime"=>$mime_type,"ext"=>$origin_ext];
}else{
throw new InternalServerError(50513);
}
}
// 读取文件获取MIME_TYPE
function getFileMIME($filename)
{
$file = fopen($filename, "rb");
$bytes4 = fread($file, 4);
fclose($file);
$strInfo = @unpack("C4chars", $bytes4);
$typeCode = dechex($strInfo ['chars1']) .
dechex($strInfo ['chars2']) .
dechex($strInfo ['chars3']) .
dechex($strInfo ['chars4']); //把十进制转换为十六进制。
switch ($typeCode) //硬编码值查表
{
case "ffd8ffe0" :
case "ffd8ffe1" :
case "ffd8ffe2" :
$type = 'image/jpeg; charset=binary';
break;
case "89504e47" :
$type = 'image/png; charset=binary';
break;
case "47494638" :
$type = 'image/gif; charset=binary';
break;
case "504B0304" :
$type = 'application/zip; charset=binary';
break;
case "25504446" :
$type = 'application/pdf; charset=binary';
break;
case "5A5753" :
$type = 'application/swf; charset=binary';
break;
case "3c3f786d" :
$type = 'application/xml; charset=binary';
break;
case "3c68746d" :
$type = 'application/html; charset=binary';
break;
case "0000" :
$type = 'text/plain; charset=binary';
break;
case "2166756e" :
$type = 'application/x-javascript; charset=binary';
break;
default :
$type = 'application/octet-stream; charset=binary';
break;
}
return $type;
}
// 获取文件扩展名
function getFileExt($type) {
switch ($type) {
case "image/jpeg; charset=binary" :
$extType = "jpeg|jpg|jpe";
break;
case "image/png; charset=binary" :
$extType = "png";
break;
case "image/gif; charset=binary" :
$extType = "gif";
break;
case "application/zip; charset=binary" :
$extType = "zip";
break;
case "application/pdf; charset=binary" :
$extType = "pdf";
break;
case "application/swf; charset=binary" :
$extType = "swf";
break;
case "application/xml; charset=binary" :
$extType = "xml";
break;
case "application/html; charset=binary" :
$extType = "html";
break;
case "text/plain; charset=binary" :
$extType = "txt";
break;
case "application/x-javascript; charset=binary" :
$extType = "js";
break;
default :
$extType = "type_error";
break;
}
return $extType;
}
// 文件扩展名验证
function fileExtCheck($muti_ext, $ext)
{
$muti_ext = explode('|',$muti_ext);
if(in_array($ext, $muti_ext)){
return $ext;
}else{
return current($muti_ext);
}
}
// 文件上传验证
function fileUploadCheck($file_error)
{
if ($file_error['temp']['error'] > 0) {
$error_mag = 'Error: ';
switch ($file_error['temp']['error']) {
case 1 :
$error_mag = $error_mag.'上传文件过大,请重试';
break;
case 2 :
$error_mag = $error_mag. '上传文件过大,请重试';
break;
case 3 :
$error_mag = $error_mag. '文件上传丢失,请重试';
break;
case 4 :
$error_mag = $error_mag. '无文件被上传,请重试';
break;
case 6 :
$error_mag = $error_mag. '文件类型不支持,请重试';
break;
case 7 :
$error_mag = $error_mag. '上传文件存储失败,请重试';
break;
}
throw new Exception($error_mag);
}
}
}
以上对 jpeg|jpg|jpe 、png、gif、zip、pdf、swf、xml、html、txt、js文件进行硬编码检测,其他的文件格式可以参考硬编码表添加就可以了。
本作品采用《CC 协议》,转载必须注明作者和本文链接
PHP 库函数本身就有获取文件 MIME 类型的方法了,例如:
string mime_content_type ( string $filename )
(http://php.net/manual/zh/function.mime-con...) 、finfo
类方法(http://php.net/manual/zh/function.finfo-fi...)