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 协议》,转载必须注明作者和本文链接
Ethan Smart & AI Algorithm
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 1

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...

5年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!