你写的防止任意文件上传漏洞的代码,不一定安全

说明:任意文件上传漏洞,很多PHP开发者也会做一些简单的防护,但是这个防护有被绕过的可能。

原生漏洞PHP示例代码:

php 复制代码
$file = $_FILES['file'] ?? [];
//检测文件类型
$allow_mime = ['image/jpg', 'image/jpeg', 'image/png', 'image/gif'];
if(! in_array($file['type'], $allow_mime)) {
    echo json_encode(['code' => 1, 'msg' => "文件类型错误"], JSON_UNESCAPED_UNICODE);
    return;
}

print_r($file);

上传一个PHP文件,提示文件类型错误,使用ApiPost修改上传的Content-Type,把原先的application/x-httpd-php修改为image/png,则可绕过。 因为:$_FILES['type']是根据上传文件的content-type获取的,并文件本身的mime-type,而content-type又可以被篡改。

原生漏洞PHP漏洞优化意见(获取临时文件的真实类型):

php 复制代码
$file = $_FILES['file'] ?? [];
//检测文件类型
$allow_mime = ['image/jpg', 'image/jpeg', 'image/png', 'image/gif'];
if(! in_array((new \finfo(\FILEINFO_MIME_TYPE))->file($file['tmp_name']), $allow_mime)) {
    echo json_encode(['code' => 1, 'msg' => "文件类型错误"], JSON_UNESCAPED_UNICODE);
    return;
}

print_r($file);

对Laravel框架,也有同样的问题,别用错函数:

php 复制代码
$file->getClientMimeType(); //相当于$_FILES['file']['type'];
$file->getMimeType(); //相当于(new \finfo(\FILEINFO_MIME_TYPE))->file($_FILES['file']['tmp_name'])

说话得有依据,经过反复的追Laravel的源码:

php 复制代码
底层对getClientMimeType()的实现:
是在vendor/symfony/http-foundation/Request.php的createFromGlobals()中,基于$_FILES做的封装。
底层对getMimeType()的实现:
是在vendor/symfony/mime/FileinfoMimeTypeGuesser.php的guessMimeType()中,利用finfo的内置PHP类实现的。

对Laravel任意文件上传漏洞优化意见(获取临时文件的真实类型):

使用getMimeType函数。

整体修复意见:

先判断文件后缀,在判断临时文件的mime类型属性,不要根据请求头判断。

扩展:

mime_content_type函数与(new \finfo(\FILEINFO_MIME_TYPE))->file('file_path')的区别?

检测文件mime类型,还有一个mime_content_type();

  1. mime_content_type()获取的mime类型,会与操作系统的mime类型有映射,意味着不同的系统可能存在一些小差别。
  2. finfo类使用了 PHP 的 FileInfo 扩展。FileInfo 扩展利用了文件的特征签名(或称为魔术数字)来检测文件的实际类型,并根据文件的内容进行精确的 MIME 类型推断。

虽然两者相差不大,但是推荐用(new \finfo(\FILEINFO_MIME_TYPE))->file('file_path');

(new \finfo(\FILEINFO_MIME_TYPE))->file('file_path')与finfo_file()的区别?

使用finfo_file()也可以获取文件的mime类型。

php 复制代码
$mime = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $file['tmp_name']);
$mime = (new \finfo(FILEINFO_MIME_TYPE))->file($file['tmp_name']);

两者底层对获取mime类型的实现无差别,展示写法不同。

什么是文件的魔术数字?

文件的魔术数字是文件头部的一段特定的字节序列,用来描述文件的类型或格式,一般用16进制表示。 文件的魔术数字一般包含一些特殊的字符和数字组成的固定长度的字节串,不同类型的文件具有不同的魔术数字。例如,PNG 图像文件的魔术数字为 89 50 4E 47 0D 0A 1A 0A,而 JPG 图像文件的魔术数字为 FF D8 FF E0 00 10 4A 46 49 46 00 01。 PHP获取魔术数字实现方案:

php 复制代码
$fileHandle = fopen($_FILES['file']['tmp_name'], 'rb');
$hex = '';
while (! feof($fileHandle)) {
    $byte = fread($fileHandle, 1);
    $hex .= sprintf("%02X ", ord($byte));
}
fclose($fileHandle);
echo $hex;
相关推荐
Fu1co1 小时前
项目源码安全审查—密钥硬编码问题
安全
脑子不好的小菜鸟2 小时前
深入剖析 Rust `HashMap`:安全哈希 (SipHash) 与高性能冲突处理 (Swiss Table)
安全·rust·哈希算法
隐语SecretFlow6 小时前
【隐语SecretFlow】由蚂蚁集团牵头制定的“隐私保护计算安全分级”IEEE国际标准已正式发布!
大数据·网络·安全
节点小宝7 小时前
节点小宝与中兴路由合作升级:AX5400系列新增远程控网功能
服务器·网络·安全·智能路由器·远程工作
xixixi777778 小时前
堡垒机(核心功能、工作流程、价值总结)
网络·安全·堡垒机
鹿鸣天涯8 小时前
等级保护标准体系再完善:六项新技术公安行标正式发布
安全
IDOlaoluo9 小时前
PHP-5.2.1.tar.gz 离线安装教程:从源码编译到配置的详细步骤(附安装包)
开发语言·php
独行soc10 小时前
2025年渗透测试面试题总结-224(题目+回答)
网络·python·安全·web安全·adb·渗透测试·安全狮
課代表12 小时前
WindoWs 系统管理批处理脚本
windows·安全·脚本·注册表·bat·命令·组策略
哈乐12 小时前
网络安全应用题3:网络攻击与防范
安全·web安全·ddos