PHP:如何正确检查文件的 MIME 类型?

Posted

技术标签:

【中文标题】PHP:如何正确检查文件的 MIME 类型?【英文标题】:PHP: How to properly check MIME type of a file? 【发布时间】:2020-05-16 01:43:46 【问题描述】:

我有一个可以上传图片的输入,唯一允许的图片类型是:

png, jpg, jpeg

在将图像插入数据库之前,它会检查图片是否为 png、jpg、jpeg。但现在出于安全原因,我需要在第一次检查之前或之后检查 MIME 类型。

我该怎么做?这是我的代码:

<?php

$iAmountOfFiles = count($_FILES['Filename']['name']);

while($iAmountOfFiles >= 1) 

    $iAmountOfFiles--;

    $aFileProperties = pathinfo($_FILES['Filename']['name'][$iAmountOfFiles]);
    if(!in_array(strtolower($aFileProperties["extension"]), $aExtensionWhitelist)) 
        echo "Bestands type niet toegestaan";
        // exit;
        continue;
    

    $sTarget = ROOT.BACKEND."/pages/bezienswaardigheden-toevoegen/uploads/";
    $sUniqueFileNameHash = hash('adler32', time().rand());
    $Filename = basename($sUniqueFileNameHash."-".$_FILES['Filename']['name'][$iAmountOfFiles]);
    $Filename = basename($aFileProperties["filename"]."-".$sUniqueFileNameHash.".".strtolower($aFileProperties["extension"]));

    // Writes the Filename to the server
    if(move_uploaded_file($_FILES['Filename']['tmp_name'][$iAmountOfFiles], $sTarget.$Filename)) 

    // here needs to come the mime check

【问题讨论】:

请阅读How to Ask。在您来这里询问之前,您应该首先自己付出一些努力。您的问题目前没有任何迹象表明您实际尝试过,但您基本上只是在这里放弃了您的要求。一个简单的“我该怎么做?” 在这里不是一个合适的提问方式。我们不在这里,因此您可以将任何研究外包给我们。 这能回答你的问题吗? Getting mime type from file name in php 这里; $result = new finfo();print $result-&gt;file($filename, FILEINFO_MIME_TYPE); 【参考方案1】:

为了获得 MIME 类型,开发者一般依赖$_FILES['input_name']['type']。但这绝对是脆弱的。因为恶意用户可以将image/jpgimage/pngimage/gif 等设置为一个实际上不是图像的文件的 MIME 类型。在这种情况下,恶意用户可能会通过您的脚本传递来上传其他文件而不是图像并出于他们的目的执行您的脚本,这很危险。

所以我建议你不要依赖下面的 sn-p 来获取文件的 MIME

$_FILE['input_name']['type'];

我建议使用mime_content_type() 函数来获取 MIME 类型,但要借助其他 PHP 的内置函数。这就是is_uploaded_file() 函数。它的作用是:

这有助于确保恶意用户没有尝试 诱使脚本处理不应该处理的文件 工作——例如,/etc/passwd。

如果有任何机会,这种检查尤其重要 对上传文件所做的任何事情都可能向 用户,甚至同一系统上的其他用户。

所以要使这个函数正常工作,它需要一个特定的参数。查看下面的代码:

if (is_uploaded_file($_FILES['input_name']['tmp_name'])) 
    // Do other stuff.

此函数成功返回true,否则返回false。因此,如果它返回true,那么您可以使用该文件。感谢这个功能。现在mime_content_type() 功能开始发挥作用。如何?看下面的代码:

if (is_uploaded_file($_FILES['input_name']['tmp_name'])) 
    // Notice how to grab MIME type.
    $mime_type = mime_content_type($_FILES['input_name']['tmp_name']);

    // If you want to allow certain files
    $allowed_file_types = ['image/png', 'image/jpeg', 'application/pdf'];
    if (! in_array($mime_type, $allowed_file_types)) 
        // File type is NOT allowed.
    

    // Set up destination of the file
    $destination = '/path/to/move/your/file/';

    // Now you move/upload your file
    if (move_uploaded_file ($_FILES['input_name']['tmp_name'] , $destination)) 
        // File moved to the destination
    

顺便说一句,对于新手,请勿尝试使用此功能获取远程 url 来获取 MIME 类型。下面的代码将不起作用:

mime_content_type('http://www.example.com/uploads/example.png');

但下面的一个可以工作:

mime_content_type('/source/to/your/file/etc.png');

希望您从现在开始喜欢上传文件。

【讨论】:

move_uploaded_file 还将检查有效的上传文件。来自docs ..."如果文件名不是有效的上传文件,则不会发生任何操作,move_uploaded_file() 将返回FALSE。" @Fahmi 是的!但在使用move_uploaded_file() 上传文件之前使用is_uploaded_file() 始终是一个好习惯。因为它确保任何恶意用户都没有试图欺骗执行正在上传文件的脚本。【参考方案2】:

您可以通过文件内容获取 mime 类型:

public static function getMimeTypeFromFileContent(string &$content): string

    return (new finfo(FILEINFO_MIME_TYPE))->buffer($content);

您可以通过$content = file_get_contents($pathToFile);获取文件内容

【讨论】:

【参考方案3】:

使用上面简单的[你可以说大一个?]函数,你可以提取或获取文件的mime类型或者你可以说内容。

但在使用此功能之前,您可能需要进行一些预配置,

就像你必须确保你在 php.ini 文件中打开或配置了 curl 扩展、文件系统相关扩展和 finfo 扩展。

这里,我简单描述一下这个函数的整个过程。

    首先,我们将所有更新的 mime 类型存储为来自官方 apache mime 类型 url 的数组。

您还可以在 apache conf 目录中获取此 mime 类型文件 安装使用 url。在这个函数中,我们使用 live url 来获取所有的 mime 类型。

    但此函数的第零个过程是验证 apache url 是否有效。

    验证 url 后,如果 url 被验证 [表示 live ],我们将来自该 url 的所有 mime 存储为一个名为 $mimes

    的数组

如果 url 不存在或不存在,我们将手动创建一个具有一些通用扩展名的数组。

    然后我们将内容验证为文件。

    然后我们检查 PHP pathinfo 函数以确保是否有任何文件扩展名。如果有,请将其存储起来。

    之后,我们检查 $mimes 数组和我们的 content extension 作为 $mimes 数组索引。

    最后我们通过$content_mime变量$mimes数组的索引值作为内容mime类型返回.

就是这样。

<?php
    /**
     * **get_content_mime_type
     *
     * @param  string $content, the content or the file whose mime type you want to know.
     * @return string
     */
    function get_content_mime_type($content)
    
        $url = 'http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types';
        $url_live = false;
        $handle = curl_init($url);
        curl_setopt_array($handle, array(
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_NOBODY => true,
            CURLOPT_HEADER => false,
            CURLOPT_RETURNTRANSFER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_SSL_VERIFYPEER => false
        ));
        $response = curl_exec($handle);
        $httpCode = curl_getinfo($handle, CURLINFO_EFFECTIVE_URL);
        $httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
        if ($httpCode == 200)
        
            $url_live = true;
        
        $url_live = $url_live;
        curl_close($handle);
        $mimes = array();
        if ($url_live)
        
            $mimes_file = file_get_contents($url);
            preg_match_all('#^([^\s]2,?)\s+(.+?)$#ism', $mimes_file, $matches, PREG_SET_ORDER);
            foreach ($matches as $match)
            
                $exts = explode(" ", $match[2]);
                foreach ($exts as $ext)
                
                    $mimes[$ext] = $match[1];
                
            
        
        else
        
            $mimes = array(
                'txt' => 'text/plain',
                'htm' => 'text/html',
                'html' => 'text/html',
                'php' => 'text/html',
                'css' => 'text/css',
                'js' => 'application/javascript',
                'json' => 'application/json',
                'xml' => 'application/xml',
                'swf' => 'application/x-shockwave-flash',
                'flv' => 'video/x-flv',
                // images
                'png' => 'image/png',
                'jpe' => 'image/jpeg',
                'jpeg' => 'image/jpeg',
                'jpg' => 'image/jpeg',
                'gif' => 'image/gif',
                'bmp' => 'image/bmp',
                'ico' => 'image/vnd.microsoft.icon',
                'tiff' => 'image/tiff',
                'tif' => 'image/tiff',
                'svg' => 'image/svg+xml',
                'svgz' => 'image/svg+xml',
                // archives
                'zip' => 'application/zip',
                'rar' => 'application/x-rar-compressed',
                'exe' => 'application/x-msdownload',
                'msi' => 'application/x-msdownload',
                'cab' => 'application/vnd.ms-cab-compressed',
                // audio/video
                'mp3' => 'audio/mpeg',
                'qt' => 'video/quicktime',
                'mov' => 'video/quicktime',
                // adobe
                'pdf' => 'application/pdf',
                'psd' => 'image/vnd.adobe.photoshop',
                'ai' => 'application/postscript',
                'eps' => 'application/postscript',
                'ps' => 'application/postscript',
                // ms office
                'doc' => 'application/msword',
                'rtf' => 'application/rtf',
                'xls' => 'application/vnd.ms-excel',
                'ppt' => 'application/vnd.ms-powerpoint',
                'docx' => 'application/msword',
                'xlsx' => 'application/vnd.ms-excel',
                'pptx' => 'application/vnd.ms-powerpoint',
                // open office
                'odt' => 'application/vnd.oasis.opendocument.text',
                'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
            );
        
        $content_mime = 'unknown';
        if (is_file($content))
        
            if (isset(pathinfo($content) ['extension']))
            
                $content_ext = pathinfo($content) ['extension'];
                if (isset($mimes[$content_ext]))
                
                    $content_mime = $mimes[$content_ext];
                
                else
                
                    if (is_readable($content) && is_executable($content))
                    
                        $finfo = finfo_open(FILEINFO_MIME_TYPE);
                        $content_mime = finfo_file($finfo, $content);
                        if ($content_mime === null | $content_mime === "")
                        
                            $content_mime = "application/octet-stream";
                        
                        else
                        
                            $content_mime = $content_mime;
                        
                        finfo_close($finfo);
                    
                    else
                    
                        $content_mime = "application/octet-stream";
                    
                
            
        
        else
        
            // return whatever you want
            // $content_mime = 'unknown';
            
        
        $content_mime = $content_mime;
        return $content_mime;
    
    ?>

【讨论】:

虽然此代码可能会回答 OP 的问题,但您可以通过附加有关您的代码如何解决问题的说明来使您的答案更好 @simas-joneliunas 是的,现在添加

以上是关于PHP:如何正确检查文件的 MIME 类型?的主要内容,如果未能解决你的问题,请参考以下文章

PHP:读取文件内容类型的问题

检查文件 mime 类型是不是与 php 中的扩展名匹配

如何使用 php 在文件上设置 mime 类型?

在 DRUPAL 中验证 PDF 文件上传中的 MIME 类型

PHP - 使用正确的 MIME 类型打开上传的 DOCX 文件

在 PHP 中检查“魔术字节”或 Mime 类型?