如何保护PHP图片上传脚本免受攻击?

7
我创建了一个简单的页面,供本地非营利出版物上传照片(使用一个脚本和来自Stack和朋友的一些帮助;我对PHP知之甚少)。
我不太擅长安全性(基于无知而非故意疏忽),但我已经采取了以下措施来保护此页面:
• PHP脚本仅接受.jpg、.png和.tif文件进行上传;
• 它保存表单内容的子文件夹权限设置为700,保存上传照片的子文件夹权限也设置为700;
• 根据文档,我的主机配置确保只有.php文件作为.php运行:
<FilesMatch \.php$>
    SetHandler php52-fcgi
</FilesMatch>

• 我已经在相关文件夹(主文件夹以及保存的内容文件夹)中放置了一个 .htaccess 文件:

RemoveHandler .php
RemoveHandler .inc
RemoveHandler .pl
RemoveHandler .cgi
RemoveHandler .py
RemoveHandler .fcgi

然而,一夜之间,有人发现了这个测试页面,并提交了一个看似完全无害的测试消息和小型.jpg文件。这是一个私人测试页面,具有非直观的URL,只有我和其他三个人知道;其他人都没有发送这个测试。

这让我担心可能存在一些不正当的行为,而且我很担心自己对安全方面了解不足,无法确保这个页面的安全性。

我是否遗漏了一些明显的事情?


用户提供的文件名(在 $_FILES['upload']['name'] 中)是不可信的,很容易被更改。不要检查这些扩展名。如果 getimagesize() 返回一个有效结果,请检查 MIME 类型。如果可能,生成自己的文件名。 - MarcDefiant
4个回答

8

处理上传文件时需注意,$_FILES数组中的所有数据都可能被伪造。它们通过HTTP传输,因此很容易将可执行文件伪装成image/jpg格式的文件。

1- 检查真实的文件类型

PHP提供了一些函数来检查文件的真实类型,你可以使用fileinfo函数。

$finfo = new finfo(FILEINFO_MIME, "/usr/share/misc/magic");
$filename = "/var/tmp/afile.jpg";
echo $finfo->file($filename);

2- 检查图片属性

你显然只想上传图像,所以接收到的文件必须有宽度和高度:

使用getImageSize()获取有关图像的所有必要信息。如果返回false,则该文件可能不是图像,您可以将其删除。 getImageSize还可以为您提供MIME类型,但我不知道是否可以信任。

2.5- 重新处理图像

如用户628405建议的那样,使用GD重新处理图像可能是更安全的做法。

$img = imagecreatefrompng('vulnerable.png'); 
imagepng($img, 'safe.png');

显然,它必须根据图像类型进行适应。请参阅php文档中的所有imagecreatefrom*。

3- 上传文件夹 除了您已经完成的内容之外:

确保您的上传文件夹不可从Web访问。验证上传的文件,如果需要,将其移动到另一个文件夹并重命名文件。 这将防止黑客执行恶意文件(如果无法通过url访问,则无法执行)。

进一步阅读:https://www.owasp.org/index.php/Unrestricted_File_Upload


2
曾经有这样一种情况,有人上传了一个包含 <?php phpinfo();?> 的二进制数据的 PNG 文件到我的 Web 服务器上。虽然这段代码不可执行,但仍可以通过 include() 函数访问。不幸的是,getimagesize() 返回了非 false 值。我通过重新保存所有上传的图像来解决这个问题,使用 $img = imagecreatefrompng('vulnerable.png'); imagepng($img, 'safe.png'); - CodingHamster
我对这一切都还比较新,但并不害怕自己做功课,但有几个问题:首先,在脚本的“case 'file'”部分中,我是否需要添加fileinfo和getImageSize命令以及文件重命名部分? - JeanSibelius
所有的检查都必须尽快在 $_FILES['my_files']['tmp_name'] 变量上完成;如果一切顺利,您可以移动文件,重命名它,并进行任何您需要的操作。 - grunk

4

不要依赖客户端的任何数据,包括内容类型!

不要将上传的文件保存在 Web 根目录中。上传的文件应该只能通过您的脚本访问,以便更好地控制。

不要使用原始文件名和扩展名保存上传的文件!将这些数据存储在数据库中以供以后检索。


0

您可以检查文件的MIME类型,但只要您的php处理程序只能执行.php文件,并且您注意不要在脚本中保存上传的.php文件,就不会暴露任何安全漏洞。

当然,这适用于.php文件以及安装在服务器上的任何其他服务器端脚本语言。

更好的做法是保留您正在接受的扩展名列表以保存在您的文件系统中。


1
这与文件上传不直接相关,但您可以将带有恶意 PHP 代码的图像作为评论上传。如果网站存在本地文件包含漏洞,您可能可以包含您的图像并执行恶意代码。 - MarcDefiant

0

我会忽略传入文件的MIME类型和文件扩展名。这些都可以被伪造。

如果您要采用这种方法,请将这些文件存储在一个目录中。

确保该目录仅用于存储图像(音乐)文件,然后编写脚本通过查看文件格式来为它们添加正确的扩展名。

还要确保该目录无法执行PHP(或其他任何东西)。

这样可以保证您的安全。


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