11

我正在编写一个脚本,该脚本将处理用户上传到服务器的内容,作为附加的安全层,我想知道:

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

每种类型/扩展是否有字节标记或某些唯一标识符?

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

4

11 回答 11

14

不是真的,不。

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

您的程序将不得不简单地尝试为您接受的每个文件类型解析文件。

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

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

编辑:我在 PHP 中看到有一些工具可以为您执行此操作。

此外,MIME 类型是用户浏览器声明文件的类型。阅读这些内容并在您的代码中对其进行操作非常方便且有用,但这不是一种安全的方法,因为任何向您发送错误文件的人都会很容易伪造 MIME 标头。这是一种前线防御,以防止期望 JPEG 的代码与 PNG 相冲突,但如果有人在 .exe 中嵌入病毒并将其命名为 JPEG,则没有理由不欺骗 MIME 类型。

于 2009-01-26T23:05:59.713 回答
9

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

如果您正在运行 PHP 5.3+,请查看Fileinfo 函数

$finfo = finfo_open(FILEINFO_MIME); 
$type = finfo_file($finfo, $filepath);
finfo_close($finfo);  

或者,查看 旧版本的mime_content_type

$type = mime_content_type($filepath);

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

于 2009-01-26T23:04:44.350 回答
2

PHP 有一个超全局的 $_FILES来保存大小和文件类型等信息。看起来该类型是从某种标题中获取的,而不是扩展名,但我可能错了。

w3schools 网站上有一个例子。

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

更新:

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

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

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

于 2009-01-26T23:10:52.357 回答
1

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

我也会有一个病毒/间谍软件扫描器,让它为你工作。

于 2009-01-26T23:04:01.933 回答
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 生效。

于 2015-02-24T07:18:00.243 回答
0

在 *nix 中,文件的前两个字节告诉你(参见“幻数”)。在 Windows 中,……有时这是真的(“标题信息”)。它最终取决于操作系统。

于 2009-01-26T23:02:42.497 回答
0

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

于 2009-01-26T23:03:07.677 回答
0

您期望什么文件类型?也许您可以检查它是否符合您的期望并拒绝其他所有内容。

于 2009-01-26T23:05:32.960 回答
0

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

于 2009-01-26T23:11:31.263 回答
0

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

MIME 类型是一个足够强大的指标吗?

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

于 2009-01-26T23:13:48.810 回答
0

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

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

  • 如果您提供上传和下载,那么一切都不重要,因为它不会执行。
  • 如果它由 Web 服务器处理,那么它将取决于 Web 服务器的配置方式,尽管受制于这些评论中的大部分内容。
  • 如果是图像,它要么显示,要么不显示,或者成为图像库攻击的目标。但只有那些。
  • 像 pdf 文件之类的东西可能不会影响您的服务器,但会影响访问该文件的人的计算机。
  • 如果它要被传递给像“system()”这样的函数,那么我们就回到操作系统的行为——就好像它被“双击”一样,甚至可能会考虑文件扩展名。
于 2009-01-26T23:21:39.963 回答