滚动新闻 →
火锅为什么能够形成不同地区的文化 河口湖台女命案 丈夫投高额保险 返台遭羁押禁见 想看鲸鱼跃出海面,可以把夏末留给加拿大特朗湾 维修店为什么会要求先读取故障码 约旦教堂遇袭酿9死 3约旦人犯案枪战中受伤 激光雷达和毫米波雷达能不能互相替代 加拿大冬季环境对房屋维护有什么影响 为什么所有的钱都不应该投入投资市场 游艇维护记录为什么会影响售价 中国自媒体人‘徽剑’遭买凶杀害 曝曾举报柬埔寨电诈集团骨干 法国球星姆巴佩被指将毛泽东与自身形象混搭引发争议 蔡康永现身蒋万安竞选现场引争议,此前曾现身沈伯洋活动 巴西股市暴跌17% 雷亚尔兑美元重贬12% 电影公司怎样通过不同合同控制明星成本 加拿大CRA账户无法登录怎么办 胡春华缺席纪念大会 内部人士称其政治前景不明 金钟奖庄凯勋、杨贵媚封帝后 《忘了我记得》大赢家 西班牙山道巴士坠崖事故致83人受伤 美国集会自由如何受到法律保护 英国为什么开始争夺北美殖民地 川普指控巴西大选遭操控 亲盟候选人落败引争议 屋檐下面为什么会出现水渍 GPU Operator 解决了 Kubernetes 中哪些问题,GPU 驱动与容器运行环境如何管理 Google Cloud committed use discounts 怎么理解,长期运行的业务是否适合使用 MySQL LIKE 搜索越来越慢,SaaS 系统应该怎样处理关键词查询 欧盟拟明年初加码对华商品限制措施 汕头莲阳大桥线缆管断裂致多人落水 死伤情况待查 台海委会研发无人机群系统应对中共灰色侵扰 微软因AI冲击推出自愿离职计划 逾6万员工可申请 西藏地震余震频发 网友发布灾情遭处罚 求救信息被封锁 三代港人自由追寻史在多伦多首映 USB接口内部针脚弯曲,自己维修之前需要了解哪些风险 Windows 11 指纹登录怎么设置?兼容设备可以这样提高登录便利性 美国司法部对48名外国人提起选举欺诈诉讼 纽约州长候选人布莱克曼赴法拉盛拉票,承诺降低生活成本 中东局势升级 美以军事部署引发伊朗担忧 中期选举监督全面升级 关键州选情胶着 美日联合军演部署反舰导弹 强化第一岛链防御 MySQL 数据库恢复怎么操作?面对误删数据时应该提前准备什么 古诗词中的酒究竟意味着什么

PHP 图片上传有哪些安全问题?文件类型、大小和路径检查不能忽视

发布时间: 2026-10-06 10:24:01    最后更新: 2026-10-26 18:56:28    阅读:112  约7 分钟阅读     

PHP 图片上传有哪些安全问题?文件类型、大小和路径检查不能忽视
图片说明:示意图   图片来源:Public Domain(公有领域)
图片上传是 PHP 网站中非常常见的功能,用户头像、文章配图、商品图片、附件以及后台编辑器中的媒体文件,都可能涉及文件上传。代码层面看起来往往只是接收 $_FILES,检查一下扩展名,然后使用 move_uploaded_file() 保存文件,但从服务器安全角度看,上传功能实际上把一份完全由外部用户控制的数据交给了服务器处理。 这意味着风险并不只存在于“上传了一个 PHP 文件”这种最典型的场景。文件类型判断错误、文件名可控、目录权限过大、Web 服务器执行上传文件、图片解析器漏洞、超大图片消耗 CPU 和内存,以及没有限制上传频率,都可能成为攻击入口。安全的 PHP 图片上传应该采用多层验证,而不是依靠一个函数或者一条扩展名规则解决全部问题。 一、不要相信浏览器提供的 MIME 类型 PHP 的 $_FILES['file']['type'] 经常被初学者拿来判断文件是不是图片。例如: if ($_FILES['file']['type'] === 'image/jpeg') { // 允许上传 } 这种判断不能作为安全边界。 这个字段来自客户端提交的数据,攻击者可以自己构造 HTTP 请求,因此完全可以把一个并非 JPEG 的文件声明成 image/jpeg。 服务器应该自行检测上传文件,而不是相信浏览器告诉服务器“我上传的是图片”。 PHP 可以使用 Fileinfo 扩展中的 finfo_file() 对文件进行 MIME 类型检测。例如网站只允许 JPEG、PNG 和 WebP,那么程序就应该明确列出允许的 MIME 类型,并拒绝其他类型。 但 MIME 检测也不能被理解成绝对可靠的“真图片认证”。它应该和扩展名、图片解码、尺寸检查以及最终存储策略结合使用。 二、扩展名应该采用白名单 文件扩展名同样需要检查,但不要采用黑名单思维。 例如只检查: if ($ext !== 'php') { // 允许 } 这种设计的问题在于攻击者可能使用其他可执行扩展名,或者利用服务器配置与应用程序对文件类型理解不同所产生的漏洞。 如果业务只需要 JPEG、PNG 和 WebP,那么程序应该直接规定: jpg jpeg png webp 其他扩展名全部拒绝。 同时,最终保存的文件扩展名最好由服务器根据经过验证的文件类型决定,而不是直接沿用用户上传时提供的文件名。 三、文件名不能直接交给服务器 下面这种写法风险很高: $target = '/var/www/uploads/' . $_FILES['file']['name']; move_uploaded_file( $_FILES['file']['tmp_name'], $target ); 这里的最终文件名完全由客户端控制。 文件名可能包含特殊字符、路径片段,也可能造成文件覆盖。如果应用进一步允许用户提交目录名称,风险会扩大到路径遍历。 安全的做法是由服务器重新生成文件名。 例如可以使用 random_bytes() 生成随机标识符,然后由服务器确定扩展名: $name = bin2hex(random_bytes(16)) . '.jpg'; 用户原始文件名如果业务上需要保存,可以作为数据库中的显示信息记录,但不应该直接成为服务器实际存储路径。 四、路径遍历不能只靠过滤 ../ 很多开发者看到路径遍历,就想到过滤: ../ 这种方法并不可靠。 文件系统路径在不同操作系统、Web 服务器和应用程序中的处理方式存在差异。安全设计不应该建立在“我已经过滤了几个特殊字符”之上。 更合理的做法是让用户完全无法决定服务器的存储路径。 例如程序固定使用: /storage/uploads/ 然后由服务器按照日期、资源 ID 或随机 ID 组织目录。 用户只负责提供文件,服务器决定文件存在哪里。 五、上传目录最好放在 Web 根目录之外 如果图片没有必要通过 URL 直接访问,可以将上传文件存放在 Web root 之外。 例如网站可以设计成: /home/site/public/ index.php css/ js/ /home/site/storage/ uploads/ 这样,即使上传目录中出现一个恶意文件,浏览器也无法通过普通 URL 直接访问它。 如果业务需要公开显示图片,则可以通过受控的静态文件路径、应用下载接口或者对象存储提供访问。 这种架构比把所有用户上传文件直接放在: /public/uploads/ 下面更容易控制风险。 六、如果上传目录必须公开访问,就禁止脚本执行 很多现有 PHP 网站已经把图片放在 Web 根目录下面,这种情况下最重要的一项措施就是确保上传目录不能执行 PHP 等服务器端脚本。 假设攻击者成功上传: test.php 如果 Apache 将其交给 PHP-FPM 执行,那么一个看似普通的图片上传漏洞就可能变成服务器端代码执行漏洞。 因此,上传目录应该按照 Web 服务器的实际配置明确禁止 PHP、CGI 等脚本执行。 同时还应该防止攻击者上传或者影响 .htaccess、web.config 等服务器配置文件。 这里需要根据 Apache、Nginx、PHP-FPM 以及主机环境分别配置,不能简单认为“文件扩展名检查通过了”就安全。 七、getimagesize() 可以辅助检查,但不能单独判断文件安全 PHP 中的 getimagesize() 经常出现在图片上传教程里。 它可以读取图片的宽度、高度以及部分图片信息,因此在判断图片尺寸方面非常有用。 但它不应该被当成“这个文件绝对是一张安全图片”的验证函数。 如果代码只是: if (getimagesize($tmp)) { move_uploaded_file($tmp, $target); } 就直接保存原始文件,安全性仍然不足。 更合理的做法是结合 Fileinfo、允许的扩展名、图片解码以及尺寸限制进行判断。 对于需要进一步处理的图片,还可以使用 GD 或 ImageMagick 等库实际解码图片,然后重新编码成网站允许的格式。 八、不要通过搜索恶意字符串来检查图片 有些教程建议使用: file_get_contents() 把整个图片读取出来,然后搜索: $maxSize) { exit('File too large'); } 随后进行扩展名、MIME、图片解码和尺寸检查。 只有这些验证全部通过之后,才进入正式存储流程。 这种分层设计比把几十项检查全部塞进一个巨大的 if 条件中更容易维护,也更方便定位上传失败原因。 十六、上传目录权限应该遵循最小权限原则 服务器上经常可以看到: chmod 777 uploads 这种处理方式。 它确实可能暂时解决“PHP 没有写权限”的问题,但代价是把目录权限放得过大。 更合理的方法是让实际运行 PHP-FPM 或 Web 服务的系统账户拥有完成业务所需的最低权限。 上传目录通常需要应用写入,但并不意味着系统中的所有用户都应该拥有写入、修改和执行权限。 也不要把 777 当成 PHP 上传问题的标准解决方案。 十七、数据库权限和文件权限是两个不同层面 如果网站使用数据库记录上传文件,那么数据库中可以保存: 文件 ID 所属用户 原始文件名 服务器生成的文件名 文件类型 文件大小 创建时间 存储状态 用户访问图片时,应用应该根据当前用户身份判断是否有权访问对应资源。 例如某个用户上传的私有文件,不能仅仅因为攻击者猜到了文件名,就可以直接获得访问权限。 随机文件名可以降低猜测概率,但不能代替访问控制。 十八、公开图片也应该考虑 Content-Type 和浏览器处理方式 对于普通 JPEG、PNG、WebP 图片,服务器应该正确返回对应的 Content-Type。 如果网站允许上传 SVG、HTML、PDF 等其他类型,则需要单独考虑主动内容、跨站脚本以及浏览器内容嗅探等问题。 如果业务根本不需要 SVG,就没有必要为了“支持更多格式”而开放 SVG。 上传系统支持的文件类型越多,需要处理的解析器和浏览器行为就越复杂。 十九、日志和异常处理不能省略 上传系统应该记录重要事件,例如谁上传了文件、上传了什么类型、验证是否通过、文件是否成功保存以及文件是否被删除。 发生异常时,还应该检查 Web 服务器、PHP-FPM 和应用程序日志。 常见问题包括: PHP 上传限制 POST 请求大小限制 临时目录不可写 正式目录权限不足 磁盘空间不足 Fileinfo 不可用 图片解析失败 图片处理库异常 PHP-FPM 超时 生产环境不应该直接把 PHP 内部错误、服务器路径或者堆栈信息显示给用户。用户只需要得到明确的失败提示,详细错误应该进入服务器日志。 二十、还需要防止上传接口被大量调用 文件上传不仅有文件安全问题,还有资源消耗问题。 假设每张图片只允许 5 MB,但攻击者可以每秒提交几十次合法图片上传请求,那么即使每一个文件都通过类型验证,也可能迅速消耗磁盘、CPU、内存和数据库资源。 因此,对公开上传接口,还应该根据用户、IP、账户、接口和业务场景设置合理的频率限制。 对于后台管理员上传,也可以设置更宽松但仍然存在的请求保护。 二十一、图片上传安全应该采用纵深防御 一个成熟的 PHP 图片上传系统,可以按照下面的顺序设计: 检查用户身份。 检查用户是否具有上传权限。 检查 CSRF。 检查 $_FILES 错误状态。 检查文件大小。 检查扩展名白名单。 使用 Fileinfo 检测 MIME 类型。 根据业务检查图片尺寸。 使用图片处理库实际解码图片。 根据需要重新编码图片。 由服务器生成随机文件名。 使用服务器确定存储目录。 将文件保存到隔离的存储位置。 确保上传目录不能执行 PHP 等脚本。 设置最小化文件系统权限。 记录上传和失败事件。 对公开上传接口实施频率限制。 定期更新 PHP、Web 服务器和图片处理库。 这里没有哪一个步骤可以单独承担全部安全责任。 文件扩展名可以被伪造,MIME 可以被伪造,图片解析可能存在漏洞,随机文件名也不能解决访问控制问题,目录权限同样不能代替类型验证。安全性来自这些措施组合之后形成的防护层。 对于普通 PHP 网站,如果业务只是上传文章图片、头像和商品图片,没有必要把系统设计得过度复杂。优先做好允许格式白名单、服务端文件大小限制、Fileinfo 检测、图片解码和重编码、随机文件名、Web root 隔离以及禁止上传目录执行脚本这几个环节,就已经能够消除大量常见风险。 图片上传功能真正难的地方,不是写出一个可以把文件从临时目录移动到 uploads 目录的 PHP 函数,而是确保这份来自互联网的、不可信数据,在进入服务器文件系统、图片解析器、Web 服务器和数据库之后,每一个环节都不会获得超出业务需要的权限。对于生产环境来说,这种思路比不断增加几个扩展名黑名单或者扫描几个恶意字符串更加可靠。

喜欢这篇报道?

使用下面的功能,方便以后继续阅读和分享 MNewsTV

设为 Google 新闻首选来源 让 Google 新闻优先显示 MNewsTV 的最新报道 ›
★ 我的收藏 查看已经收藏的文章
关于文章收藏 收藏不需要注册帐号,收藏信息仅保存在当前浏览器中。 删除收藏请进入「我的收藏」进行管理。
分享这篇报道

捐助(Paypal): https://www.paypal.me/observeccp
订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP