【问题标题】:How to check file types of uploaded files in PHP?如何在 PHP 中检查上传文件的文件类型?
【发布时间】:2010-09-23 13:37:29
【问题描述】:

在 PHP 网站上,他们建议的唯一真正的检查是使用 is_uploaded_file()move_uploaded_file()here。当然,出于各种原因,您通常不希望用户上传任何类型的文件。

因此,我经常使用一些“严格”的 mime 类型检查。当然这是非常有缺陷的,因为通常 mime 类型是错误的,用户无法上传他们的文件。它也很容易伪造和/或更改。除此之外,每个浏览器和操作系统都以不同的方式处理它们。

另一种方法是检查扩展名,当然比mime类型更容易更改。

如果你只想要图片,使用getimagesize() 之类的就可以了。

其他类型的文件呢? PDF、Word 文档或 Excel 文件?甚至是纯文本文件?

编辑:如果您没有 mime_content_typeFileinfo 并且 system("file -bi $uploadedfile") 给您错误的文件类型,还有哪些其他选项?

【问题讨论】:

  • getimagesize() 函数明确指出您不应使用此函数来验证图像是否为图像。 Do not use getimagesize() to check that a given file is a valid image. Use a purpose-built solution such as the Fileinfo extension instead.php.net/manual/en/function.getimagesize.php

标签: php validation file-upload mime-types


【解决方案1】:

看看mime_content_typeFileinfo。这些是内置的 PHP 命令,用于通过查看文件的内容来确定文件的类型。还要查看上面两页的cmets,还有一些其他的好建议。

就我个人而言,我很幸运地使用了基本上是 system("file -bi $uploadedfile") 的东西,但我不确定这是否是最好的方法。

【讨论】:

  • system("file -bi -- ".escapeshellarg($uploadedfile)) 更安全。
  • 是的,我在里面做了一些转义,总是要小心PHP中的注入攻击,但是我懒得回去检查我使用的命令是什么。
  • 我投了反对票,因为您实际上没有提供代码的演示 sn-p 和在其自己的文档页面上没有演示 sn-ps 的引用代码。请更新您的答案和回复,如果可行,我很乐意投票。
【解决方案2】:

恕我直言,所有 MIME 类型检查方法都没用。

假设你有哪个应该具有 MIME 类型 application/pdf。标准方法试图找到看起来像 PDF 标题的东西(%PDF- 或类似的东西),他们将在成功时返回“好的,似乎这是一个 PDF 文件”。但实际上这并不意味着什么。您可以上传仅包含%PDF-1.4 的文件,它将通过 MIME-check。

我的意思是,如果文件具有预期的 MIME 类型 - 它将始终通过 MIME 类型检查,否则结果未定义。

【讨论】:

  • 任何认为这个答案不正确的人,read this。大开眼界。
  • mime 检查并非完全没用。在用户上传未损坏文件的情况下仍然有用。
【解决方案3】:

我假设您将有一个您将接受的文件类型的固定白名单。

对于这些类型中的每一种,您将不得不使用不同的技术来验证它们是否是该格式的有效示例。

有两个相关的问题:

  • 它看起来大概是正确的类型吗? (对于 JPEG,您可以检查标题,如您所提到的。对于许多基于 Unix 的格式,您可以检查“魔术 cookie”。)

  • 它实际上是该类型的有效示例吗(例如,对于任何类似 XML 的格式,您可以根据 DTD 进行验证。)

我认为,对于每种格式,您应该针对每种格式提出单独的问题,因为与 ZIP 文件相比,PDF 的答案会大不相同。

【讨论】:

  • 如问题所述,getimagesize() 可以完美地处理图像。
【解决方案4】:

我使用了与 PHP 5.2 兼容的 mime_content_type,因为我既不能使用 Fileinfo(它需要 PHP 5.3)也不能使用 system(),这是我的提供商禁用的。 例如,我检查文件是否为文本文件:

if (strcmp(substr(mime_content_type($f),0,4),"text")==0) { ... }

你可以在我的“PHP Directory and Subdirectory Listener & File Viewer and Downloader”中看到一个完整的例子: http://www.galgani.it/software_repository/index.php

【讨论】:

    【解决方案5】:
    if(isset($_FILES['uploaded'])) {
        $temp = explode(".", $_FILES["uploaded"]["name"]);
    
        $allowedExts = array("txt","htm","html","php","css","js","json","xml","swf","flv","pdf","psd","ai","eps","eps","ps","doc","rtf","ppt","odt","ods");
    
        $extension = end($temp);
        if( in_array($extension, $allowedExts)) {
           //code....
    
        } else {
            echo "Error,not Documentum type...";
        }
    }
    

    【讨论】:

    • 您不应该使用扩展名验证文件类型!这根本不安全,您应该获得 tmp 文件的 mime/type。同样使用客户端发送的 mime 类型也不安全。
    • 如果我编码了病毒,并重命名为 jpg。你死了。
    • @Deviance 您仍然需要找到一种方法来执行该文件,不是吗?
    • @Jacob Sanchez ,只需要知道payload uri地址即可执行。通常对于图像,它旨在显示在网站的某个位置。攻击者无论如何都会找到路径。
    • 是的,但是如果文件具有 jpg 扩展名,您将如何让服务器执行该文件?
    【解决方案6】:

    这是来自 iZend 的函数 file_mime_type

    function file_mime_type($file, $encoding=true) {
        $mime=false;
    
        if (function_exists('finfo_file')) {
            $finfo = finfo_open(FILEINFO_MIME);
            $mime = finfo_file($finfo, $file);
            finfo_close($finfo);
        }
        else if (substr(PHP_OS, 0, 3) == 'WIN') {
            $mime = mime_content_type($file);
        }
        else {
            $file = escapeshellarg($file);
            $cmd = "file -iL $file";
    
            exec($cmd, $output, $r);
    
            if ($r == 0) {
                $mime = substr($output[0], strpos($output[0], ': ')+2);
            }
        }
    
        if (!$mime) {
            return false;
        }
    
        if ($encoding) {
            return $mime;
        }
    
        return substr($mime, 0, strpos($mime, '; '));
    }
    

    【讨论】:

      【解决方案7】:

      对于PHP>=5.3.0,可以使用php的finfo_file(finfo_file)函数来获取文件的文件信息。

      对于PHPfile命令来获取文件信息。

      所以只需在一个函数中实现,

      var_dump(mime_type("wiki templete.txt"));   // print string(10) "text/plain"
      
      function mime_type($file_path)
      {
          if (function_exists('finfo_open')) {
              $finfo = new finfo(FILEINFO_MIME_TYPE, null);
              $mime_type = $finfo->file($file_path);
          }
          if (!$mime_type && function_exists('passthru') && function_exists('escapeshellarg')) {
              ob_start();
              passthru(sprintf('file -b --mime %s 2>/dev/null', escapeshellarg($file_path)), $return);
              if ($return > 0) {
                  ob_end_clean();
                  $mime_type = null;
              }
              $type = trim(ob_get_clean());
              if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\.]+)#i', $type, $match)) {
                  $mime_type = null;
              }
              $mime_type = $match[1];
          }
          return $mime_type;
      }
      

      MimeTypes

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-10-08
        • 1970-01-01
        • 2013-01-04
        • 1970-01-01
        • 1970-01-01
        • 2015-07-21
        • 1970-01-01
        • 2012-01-29
        相关资源
        最近更新 更多