很多系统重需要使用到文件上传功能,如果有人故意将文件的后缀名改成符合要求的文件,比如.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 文件进行硬编码检测,其他的文件格式可以参考硬编码表添加就可以了。
来源于https://learnku.com/articles/18593
如果本文对你有帮助,欢迎打赏本站