在PHP中检测MIME类型失败

4
我有以下PHP代码,可以显示上传文件的MIME类型。
<?php

if ($_POST) {

    var_dump($_FILES);

    $finfo = new finfo(FILEINFO_MIME_TYPE);

    var_dump($finfo->file($_FILES['file']['tmp_name']));

} else{
    ?>
    <form method="POST" enctype="multipart/form-data"><input name="file" type="file"><input name="submit" value="send" type="submit"/></form>
    <?php
}

使用此脚本上传 somefile.csv 的结果如下。
array (size=1)
    'file' =>
    array (size=5)
        'name' => string 'somefile.csv' (length=12)
        'type' => string 'text/csv' (length=8)
        'tmp_name' => string '/tmp/phpKiwqtu' (length=14)
        'error' => int 0
        'size' => int 3561
string 'text/x-fortran' (length=14)

当然,MIME类型应该是text/csv。但是我使用的框架(Symfony 1.4)使用fileinfo方法。
而且我进一步测试了一下,在Ubuntu上运行file --mime-type somefile.csv命令返回somefile.csv: text/x-fortran,而mimetype somefile.csv命令返回somefile.csv: text/csv。somefile.csv是用MSOffice创建的(我不知道这是否重要)。显然,mimetype使用了一些很棒的mime数据库(http://freedesktop.org/wiki/Software/shared-mime-info),而file没有使用。
  1. PHP使用file还是mimetype或都不用吗?
  2. 此外,我不确定该做什么;我的上传文件格式错误吗?我需要使用不同的MIME数据库吗?PHP有漏洞吗?发生了什么?

编辑:

之所以被检测为Fortran程序,是因为somefile.csv只包含以下内容:

somecolumn;
C F;

我相信上面的CSV文件内容是有效的,对吗?如果一个字段包含空格,那么这个字段不必被放在引号内,对吗?
2个回答

6

我这里没有Unix系统的电脑来检查真正的“魔法”文件(用于猜测mime类型的签名数据库),但是快速的谷歌搜索揭示了这个:

# $File: fortran,v 1.6 2009/09/19 16:28:09 christos Exp $
# FORTRAN source
0       regex/100       \^[Cc][\ \t]    FORTRAN program
!:mime  text/x-fortran

显然,它会扫描文件开头,寻找以单个C字母加空格开头的行,这些行似乎是Fortran风格注释。因此会出现误报:
somecolumn;
C F;

那么,我应该如何处理假阳性?我知道解决方案是在每个单元格周围加引号,但这不是我想要的,因为我的网络应用程序的用户会上传这些CSV文件。并且示例显示的是有效的CSV文件。 - meijuh
根据您的确切需求而定,但在这种情况下,最好也使用文件扩展名。您还可以从MIME文件中删除Fortran。(如果您已经知道它是CSV,则不确定为什么要在此处使用启发式;猜测MIME类型不会验证文件) - Álvaro González
CSV文件是应用程序的用户上传的。如果猜测MIME类型导致误报,则使用MIME类型猜测就没有意义了。我会确保文件在公共文件夹中不可执行,用户应该知道他们正在下载什么。另外,由于我只使用CSV文件,并且CSV文件的语法必须正确,因此我还可以使用其BNF语法检查CSV文件的内容。 - meijuh

0

来自PHP Mimetype介绍

该扩展已被弃用,因为PECL扩展Fileinfo以更加清晰的方式提供了相同的功能(甚至更多)。

此模块中的函数尝试通过查找文件内特定位置的某些魔术字节序列来猜测文件的内容类型和编码。虽然这不是一种百分之百可靠的方法,但使用的启发式算法非常出色。

该扩展派生自Apache mod_mime_magic,后者本身基于由Ian F. Darwin维护的file命令。有关历史和版权信息,请参见源代码。

来自PHP Fileinfo介绍

此模块中的函数尝试通过查找文件内特定位置的某些魔术字节序列来猜测文件的内容类型和编码。虽然这不是一种百分之百可靠的方法,但使用的启发式算法非常出色

这里有一个关于同一主题的问题及其答案:在PHP中检测MIME类型


http://pear.php.net/package/MIME_Type 提供与 file_info 相同的结果。我不理解为什么 CSV 文件会被识别为 fortran 文件。 - meijuh
看了Fortran代码示例,我无法弄清楚为什么会发生这种情况,它们完全不同。如果您在简单的文本编辑器中打开特定的CSV文件,它是否看起来像纯粹的CSV,还是有其他元素可能导致混淆的结果? - Rolando Isidoro
另外5美分,我谷歌了一下已经建立良好的基于PHP的Web应用程序,这里有另一种方法:Drupal 8似乎使用Guzzle PHP框架来完成此任务,请查看它们在https://github.com/guzzle/guzzle/blob/master/src/Guzzle/Http/Mimetypes.php上的代码。他们只是根据一组预定义的已知mime-type列表进行简单的扩展检查。我不认为它很牢靠。 - Rolando Isidoro
我编辑了我的初始帖子。我找到了足够少的内容,使CSV文件看起来像Fortran代码。我也相信这个内容对于CSV文件是有效的。该怎么办? - meijuh
从您的文件内容来看,我无法判断它是否为CSV文件,因为它不遵循RFC 4180定义。那更像是“以分号结尾的空格分隔值”。您可以在Wikipedia上阅读有关CSV文件缺乏标准格式的一些考虑。 - Rolando Isidoro
2.4 状态说明:“空格被视为字段的一部分,不应被忽略。” 我认为这是有效的 CSV 格式。我认为一个包含空格的字段不应该加上引号。此外,如果我使用 LibreOffice 打开文件,然后将其另存为不同的 CSV 文件,它也不会在单元格周围添加引号。 - meijuh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接