web学习笔记编程技术学习与分享

文件上传的安全边界

2026 年 06 月 30 日约 3 分钟阅读 后端与数据库

文件上传是 Web 应用里攻击面最大的功能之一:传上去的不是文件,而是可能被执行的代码。

第一道:类型校验

扩展名和 Content-Type 都由客户端控制,不可信。校验顺序:

  1. 扩展名白名单(不是黑名单——黑名单永远漏);
  2. 服务端探测真实类型:图片用 getimagesize() 或文件头魔数;
  3. 拒绝 phtml、php5、shtml 这类变体。
$allow = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'pdf', 'zip', 'docx'];
$ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
if (!in_array($ext, $allow)) reject('不支持的类型');

第二道:存储隔离

上传目录必须不可执行,这是最重要的一条。即使校验被绕过,文件也无法变成代码:

# Apache:目录内禁止脚本处理
<FilesMatch "\.(php|phtml|php[0-9]|shtml)$">
  RemoveHandler .php .phtml .php3 .php4 .php5 .php7 .php8 .phtml .shtml
  RemoveType .php .phtml .shtml
</FilesMatch>
Options -ExecCGI -Indexes

Nginx 对应 location ~* ^/uploads/.*\.(php|phtml)$ { deny all; }。

如果资源走 CDN 或对象存储,问题更小:OSS 上的文件永远只是静态资源。

第三道:文件名与路径

  • 重命名为随机名,绝不使用用户提供的文件名——它可能包含 ../(路径穿越)或特殊字符;
  • 限制大小;
  • 存储目录与 Web 根分离或至少不可枚举。
$stored = bin2hex(random_bytes(16)) . '.' . $ext;

第四道:内容层风险

  • SVG 可以内嵌脚本,作为图片展示没问题,直接内联到页面会执行 XSS,要么拒绝要么净化;
  • 图片可以携带多态 PHP(注释里的代码),所以「不可执行」必须真正生效;
  • ZIP 解压要检查压缩包内文件名,防 Zip Slip。

上传接口本身

  • 必须登录 + CSRF 防护;
  • 限速(防刷满磁盘);
  • 返回的 URL 不要直接拼接用户输入。

按这个清单逐项检查一遍,绝大多数上传漏洞都能在代码评审阶段被发现。最后一道保险是部署后的定期扫描:对上传目录做可疑特征扫描,及时清掉漏网的。

安全PHP文件上传