如何在PHP中读取已上传文件的标题?

6
我们能否在PHP中读取文件的头信息以确定上传的文件类型呢?
我不想依赖于$_FILES['control_name_from_client']['type']。因为我们知道,这个属性通过读取上传文件的扩展名来确定文件类型。
如果用户重命名了一个文件,比如说将test.jpg改成test.xls。那么在这种情况下,$_FILES['control_name_from_client']['type']会显示类型为application/vnd.ms-excel而不是image/jpeg。如果代码需要执行读取XLS文件以获取数据进行处理,则很自然地会出现问题。
请问有什么建议吗?
4个回答

8

尝试使用 finfo_file() 函数。需要传递文件路径作为参数调用该函数,例如:

$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['control_name_from_client']['tmp_name']);
finfo_close($finfo);

您需要安装 Fileinfo 扩展。正如 PHP 手册所述:

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


1
mime_content_type() 的结果基于文件扩展名,而对于 finfo_file() 我不太确定。 - Kae Verens
@Kae:你说得对。我认为Fileinfo扩展很聪明。 - lorenzo-s

2
据我所知,PHP中没有这样的函数,但如果您可以访问CLI(并且正在运行Linux),则可以通过system()使用“file”命令。

2

在旧版本的PHP中,曾经有一个名为mime_magic的扩展程序,但现在已被弃用,取而代之的是finfo_file,它使用文件签名来测试文件类型,而不仅仅是扩展名。


2

我不知道你所说的“头文件”是什么,但从安全角度来看,你真正需要注意的只是文件名扩展名。因为你的Web服务器会通过它来判断你的文件。

要测试上传的文件是否为特定应用程序的有效数据,您必须使用该应用程序特定的例程,在PHP中没有通用的工具。对于图像,可以使用ImageMagick,对于MP3文件,可以使用getID3,对于电影等等可以使用FFmpeg。

但当然,整个文件都必须被使用,仅检查“头文件”不能保证整个文件是有效的。


1
不够好。例如:上传名为“test.php.blah”的PHP文件 - 默认的Apache+PHP安装会忽略“.blah”,并将该文件视为PHP文件。您不能依赖扩展名。 - Kae Verens
@Col.Shrapnel http://verens.com/2008/10/13/security-hole-for-files-with-a-dot-at-the-end/ <-- 这是我发现这个问题的方式。基本上,一个默认的 Apache 安装会将拥有多个扩展名的文件视为最后一个扩展名是语言(例如,index.html.en),但它并不检查它是否是一个真正的语言。因此,你可以上传一个伪装成图像的 PHP 脚本(index.php.jpg),并且它将作为一个 PHP 脚本运行。 - Kae Verens
@KaeVerens 嗯,使用 .blah 是可能的。但是使用 .jpg 不行。就像我说的,检查一下你的扩展名。 - Your Common Sense
@Kae,嗯,你是对的,我道歉。这个行为看起来很疯狂。 - Your Common Sense
1
@KaeVerens 我认为你注意到的行为现在已经被纠正了。我按照你建议的确切步骤进行了操作。但是,谢天谢地,它没有运行 phpinfo()。相反,它显示了损坏的图像。我肯定想知道是否有什么我错过了,或者你是否仍然可以以某种方式重现这个问题。 - Bhavik Shah
显示剩余2条评论

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