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 文件进行硬编码检测,其他的文件格式可以参考硬编码表添加就可以了。

来源于https://learnku.com/articles/18593

支付宝扫码打赏 微信扫码打赏

如果本文对你有帮助,欢迎打赏本站

喜欢 ()or分享