如何以编程方式确定文件的真实扩展名/类型?

时间:2009-01-26 22:59:44

标签: php security file-upload file-type

我正在开发一个处理用户上传到服务器的脚本,并且作为一个额外的安全层我想知道:

有没有办法检测文件的真实扩展名/文件类型,并确保它不是另一种用不同扩展名掩盖的文件类型?

每种类型/扩展名都有字节戳或一些唯一标识符吗?

我希望能够检测到有人没有在他们上传的文件中应用不同的扩展程序。

谢谢,

11 个答案:

答案 0 :(得分:13)

不是,不。

您需要读取每个文件的前几个字节,并将其解释为有限的一组已知文件类型的标头。大多数文件具有不同的文件头,前几个字节中的某种元数据或MP3的前几千字节。

您的程序只需尝试为每个接受的文件类型解析文件。

对于我的程序,我将上传的图像发送到try-catch块中的imagemagick,如果它爆炸,那么我猜这是一个糟糕的图像。这应该被认为是不安全的,因为我将任意(用户提供的)二进制数据加载到外部程序中,该程序通常是攻击向量。在这里,我相信imageMagick不对我的系统做任何事情。

我建议为您打算使用的重要文件类型编写自己的处理程序,以避免任何攻击媒介。

编辑:我在PHP中看到有一些工具可以帮到你。

此外,MIME类型是用户的浏览器声明文件的类型。在您的代码中读取它们并对它们进行操作是很方便和有用的,但它不是一种安全的方法,因为任何向您发送错误文件的人都会轻易伪造MIME标头。这是一种前线防御,以保持您的代码在PNG上禁止JPEG,但如果有人在.exe中嵌入病毒并将其命名为JPEG,则没有理由不欺骗MIME类型。

答案 1 :(得分:9)

PHP有两种方法可以读取文件内容以确定其MIME类型,具体取决于您使用的PHP版本:

如果您正在运行PHP 5.3 +

,请查看Fileinfo functions
$finfo = finfo_open(FILEINFO_MIME); 
$type = finfo_file($finfo, $filepath);
finfo_close($finfo);  

或者,查看旧版本的mime_content_type

$type = mime_content_type($filepath);

请注意,如果您想要真正安全,仅仅验证文件类型是不够的。例如,某人可以上传有效的JPEG文件,该文件利用了常见渲染器中的漏洞。为防止这种情况发生,您需要一台维护良好的病毒扫描程序。

答案 2 :(得分:2)

PHP有superglobal $_FILES,其中包含大小和文件类型等信息。它看起来像某种类型的标题,而不是扩展,但我可能是错的。

w3schools site上有一个例子。

我将测试当我有机会时它是否可以被欺骗。

更新:

其他人可能都知道这一点,但可以欺骗$ _FILES。我能够以这种方式确定它:

$arg = escapeshellarg( $_FILES["file"]["tmp_name"] );
system( "file $arg", $type );
echo "Real type:  " . $type;

它基本上使用Unix的file命令。可能有更好的方法,但我有一段时间没有使用PHP。如果可能,我通常会避免使用系统命令。

答案 3 :(得分:1)

仍然可以伪造。我会确保你不能(或不)自动运行上传到服务器的文件。

我也有一个virus/spy ware scanner,让它为你完成工作。

答案 4 :(得分:1)

您可以使用下面的代码,如果您更改了扩展名,则会为您提供MIME类型

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

Windows用户:只需编辑php.ini并取消注释此行:

extension=php_fileinfo.dll

请记住重新启动Apache以使新的php.ini生效。

答案 5 :(得分:0)

在* nix中,文件的前两个字节告诉你(参见“幻数”)。在Windows中,...有时这将是真的(“标题信息”)。最终是O.S.依赖

答案 6 :(得分:0)

可执行文件通常在第一个字节上有“签名”;我觉得很难确定文件类型到底是什么。

答案 7 :(得分:0)

您期望哪些文件类型?也许你可以检查它是否符合你的期望并拒绝其他一切。

答案 8 :(得分:0)

其他人已经提到了FileInfo,我认为这是正确的解决方案,但我会添加它,以防万一你出于某种原因不能使用它。大多数(全部?)* nix发行版都包含一个名为file的命令,当在文件上运行时会输出其类型。它具有以人类可读格式(默认)或MIME类型输出的开关。您可以让脚本在上载的文件上调用此程序并读取结果。同样,这不是首选方法。如果你在Windows上,可以通过Cygwin获得该实用程序。

答案 9 :(得分:0)

是否足够简单地检查MIME类型? 我假设更改文件的扩展名不会改变它的MIME类型?

MIME类型是否足够强大,可以在这里找到?

感谢迄今为止的所有回复。

答案 10 :(得分:0)

  

是否足够简单地检查MIME类型?我假设更改文件的扩展名不会改变它的MIME类型? MIME类型是一个足够强大的指标吗?

这实际上取决于它的使用方式。

  • 如果您提供上传和下载,那么没有任何问题,因为它不会执行。
  • 如果它是由Web服务器处理的,那么它将取决于Web服务器的配置方式,但要受其他大部分注释的影响。
  • 如果是图像,它将显示或不显示,或成为图像库漏洞利用的目标。但只有那些。
  • 像pdf文件这样的东西可能不会影响您的服务器,而是影响访问该文件的人的计算机。
  • 如果它将被传递给像“system()”这样的函数,那么我们就会回到操作系统的行为 - 就像它被“双击”一样,甚至可能会考虑文件扩展名。