MIME类型欺骗

16

在php中检查mime类型相当容易,但据我所知mime可以被伪造。攻击者可以上传带有jpeg mime类型的php脚本。一个想法是检查上传文件的文件扩展名并确保其与mime类型匹配。所有这些都假定上传目录是浏览器可访问的。

问题:除了mime类型欺骗外,还有哪些方法可以防止“坏文件”进入?

答:除了检查mime类型外,还可以使用以下技术来预防“坏文件”的上传:
1. 文件内容分析:通过对文件内容进行分析,例如使用AntiVirus软件对文件进行扫描,以检测是否存在恶意代码。
2. 白名单过滤:限制上传文件的类型,只允许上传预先定义的白名单中的文件类型,拒绝其他类型的文件。
3. 上传文件大小限制:限制上传文件的最大大小,以避免上传过大的文件。
4. 防火墙:使用网络防火墙或应用程序防火墙来保护服务器免受攻击。
需要注意的是,这些技术仅能降低攻击的风险,并不能完全消除。因此,仍需要谨慎处理上传文件。

3
你的问题是关于MIME类型欺骗,还是只想知道如何验证上传的文件以检查它们是否为图像文件? - CodeCaster
1
事实上,你不应该首先依赖 MIME 类型和文件扩展名(而且你的检查,如果 mime->extension,则是错误的;如果我伪造 MIME,我甚至可以更容易地伪造相应的文件扩展名)。 - Damien Pirsy
@CodeCaster:如何确保上传的文件的MIME类型没有被篡改? - abruski
@DamienPirsy:你会依靠什么?你如何确保不会上传恶意文件? - abruski
@Pekk:我考虑把.htaccess文件放在上传目录中。这似乎是最安全的方式。 - abruski
显示剩余2条评论
4个回答

13

简短回答:不行。

较长回答:

比较扩展名并确保其与MIME类型匹配并不能真正防止任何事情。正如评论中所说,修改文件扩展名甚至更容易。 MIME类型和扩展名仅被认为是提示,其中没有固有的安全性。

确保传入的文件不会造成伤害非常依赖于您打算使用它们的目的。在这种情况下,我理解您正在等待图像。因此,您可以先进行一些合理性检查:扫描前几个字节,以查看文件是否包含相关图像头签名-所有相关图像格式都具有这些。

“签名标头”帮助您确定文件试图模拟哪种图像格式。在下一步中,您可以检查其余内容是否符合底层图像格式。这将确保文件确实是该特定格式的图像文件。

但即使如此,文件也可能以精心制作的方式制作,以便在显示图像时,用于显示该图像的流行库(例如libpng等)会遇到攻击者在该库中找到的缓冲区溢出。

不幸的是,除了根本不允许来自客户端的任何输入之外,没有任何方法可以积极防止这种情况。


这些图片只是示例。实际上将处理PDF文件。但我想把问题概括,以便对其他文件类型也有帮助。 - abruski
1
原则始终如一。聪明的攻击者会给你想要的东西,但是以一种方式来制作文件,使其利用最流行的用于查看文件的库。对于PDF文件,这意味着它们可能嵌入Javascript、视频、图像等...数据格式提供的功能越多,选项数量就会急剧增加。如果您(或您的客户!)从未“执行”(查看也是“执行”),上传的内容,那么您才是安全的。 - emboss
可以使用像GD这样的库或像ImageMagick这样的工具将图像复制到新图像中,从而获得“安全”的图像。据我所知,这些库中没有已知的开放式漏洞 - Windows的GDI曾经有一个漏洞,但早已修复。虽然理论上不存在绝对的安全性,但确实有方法以安全的方式处理文件。 - Pekka

5

注意 - 此答案已过时

getimagesize的文档明确指出“不要使用getimagesize()来检查给定文件是否为有效图像。”

图片情况

  • 使用允许的文件扩展名列表(例如“.jpg”,“.jpeg”,“.png”)检查扩展名
  • 运行getimagesize检查上传的文件本身,如果不是图像则返回FALSE。

其他类型的上传

  • 检查允许的扩展名(例如“.pdf”)
  • 检查文件的mime类型是否与扩展名相对应

示例代码:

function getRealMimeType($filename) {
    $finfo = new finfo(FILEINFO_MIME, "/usr/share/misc/magic"); 
    if (!$finfo) {
        echo "Opening fileinfo database failed";
        return "";
    }
    return $finfo->file($filename);
}

请参阅finfo_file文档。


它不必是图像,可以是任何类型的文件。 - abruski
我明白了,让我进一步解答。 - stivlo
如果getimagesize返回false,那就太好了,如果它不是一张图片的话:-) 这对于检查我猜很有用,但是如果扩展名被欺骗了,mime类型也是如此呢? - Marshall Mathews
2
getimagesize 的文档明确指出:"不要使用 getimagesize() 来检查给定的文件是否为有效图像。而应该使用专门的解决方案,如 Fileinfo 扩展。" - chintogtokh
1
谢谢指出,我已在答案顶部添加了警告--2011年的文档没有提到这一点--请参见错误/异常 https://web.archive.org/web/20111224150939/http://php.net/manual/en/function.getimagesize.php - stivlo

-1

为了安全起见,"mime_content_type"和"exif_imagetype"不应用于安全目的,因为它们都允许欺骗文件!

更多详细信息请参见以下链接: https://straighttips.blogspot.com/2021/01/php-upload-spoofed-files.html

如果要在 "public_html" 文件夹中上传文件,则检查文件扩展名以阻止危险文件扩展名(例如 ".php")是最好的方法!

杀毒软件扫描也是一个不错的替代方案,因为一些欺骗文件会被杀毒软件检测到!


-8

检查文件扩展名。

<?php

$okFiles = array('jpg', 'png', 'gif');

$pathInfo = pathinfo($filename);

if(in_array($pathInfo['extension'], $okFiles)) {
    //Upload
} 
else {
    //Error
}

?>

就像你所说的那样,你也可以检查扩展名与MIME类型是否匹配,但只检查扩展名要简单得多。

顺便问一句,你为什么关心MIME类型呢?


为确保上传的文件与其外观相符,请注意以下内容。想象一下:上传MIME图像,但内部包含php和php代码的扩展名;上传目录可以通过浏览器访问,我想攻击者可以自由运行一些恶意代码。 - abruski
1
@abruski,所以你根本不需要检查MIME类型。只需检查扩展名。如果是jpg / png / gif,则可以,否则抛出错误“仅限jpg / png / gif”。不知道这是否是你担心的问题,但如果扩展名为.jpg而MIME说PHP时,当您浏览文件时它仍将被视为图像。 - Sawny
也要考虑到这一点。只是想确保我走在正确的轨道上。 - abruski

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