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

PHP: How to properly check MIME type of a file?

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

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

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

所以我建议您不要依赖以下代码片段来获取文件的 MIME

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

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

This is useful to help ensure that a malicious user hasn't tried to trick the script into working on files upon which it should not be working--for instance, /etc/passwd.

This sort of check is especially important if there is any chance that anything done with uploaded files could reveal their contents to the user, or even to other users on the same system.

因此,要使此功能正常工作,它需要一个特定参数。查看下面的代码:

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

此函数 returns true 成功,false 否则。因此,如果它 returns 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');

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

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

但是之前,为了使用这个功能你可能需要做一些预配置,

Like you have to sure that you turned or configured curl extension, filesystem releted extension and finfo extension in php.ini file.

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

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

You can also get this mime type file in your apache conf directory insted of using url. In this function we are using live url to get all the mime type.

  1. 但是此函数的第零个进程 是验证 apache url 是否处于活动状态。

  2. 在验证 url 之后,如果 url 被验证 [意味着 live],我们将来自 url 的所有哑剧存储为一个名为 $哑剧

If the url isn't live or exist we are manually making an array with some common extension available.

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

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

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

  4. 最后 我们返回 $mimes 数组 的索引值作为 content mime 类型 通过 $content_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;
    }
    ?>

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

public static function getMimeTypeFromFileContent(string &$content): string
{
    return (new finfo(FILEINFO_MIME_TYPE))->buffer($content);
}

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

获取