html文件上传接口本身无漏洞,问题在于服务端未严格校验:仅依赖前端js、accept属性、$_files['type']或扩展名过滤均不可靠;必须用finfo_file()检测魔数、重命名文件、禁用上传目录脚本执行、存储于web根目录外,并对路径做标准化与白名单约束。

HTML 文件上传接口本身没有漏洞,问题出在服务端如何处理 input[type=file] 提交上来的数据。只要后端没做严格校验,前端再怎么隐藏、禁用、混淆,都挡不住攻击者直接发一个 curl 或用 Burp Suite 改包上传 shell.php。
为什么禁用 JavaScript 校验后上传立刻失败?
这是典型“只做客户端校验”的信号。浏览器里看到的文件类型限制、扩展名弹窗、accept="image/*" 属性,全在 JS 或 HTML 层面,攻击者按 F12 → 禁用 JS → 选任意文件 → 点上传,请求照样发出。服务端如果只依赖 $_FILES['file']['type'] 或 event.target.files[0].type 做判断,就等于把门锁换成了贴纸。
-
accept属性仅影响文件选择器弹窗的过滤,可被绕过 -
event.target.files[0].type是浏览器根据文件扩展名和系统注册表推测的,不可信 - 所有前端校验逻辑(包括大小、后缀、MIME)必须在服务端重复执行,且不能照搬前端值
服务端校验时 $_FILES['file']['type'] 为什么不能信?
这个字段来自 HTTP 请求头里的 Content-Type,由浏览器填充,攻击者在抓包时能随意改成 image/png、text/plain 甚至空字符串。PHP 的 $_FILES 数组里这个值根本不参与文件内容解析,纯属“听用户说”。真实场景中,上传一个改名为 shell.php.jpg 的文件,$_FILES['file']['type'] 很可能还是 image/jpeg —— 因为浏览器真把它当图看了。
- 真正该用的是
finfo_file()检测文件魔数(magic number),比如jpg必须以FF D8 FF开头 - PHP 中避免用
pathinfo($filename, PATHINFO_EXTENSION)取扩展名,应先重命名再存,否则shell.php.jpg可能被 Apache/NGINX 按后缀解析执行 - 不要对
$_FILES['file']['type']做白名单匹配,它连基本可信度都没有
上传路径和目录权限配置错在哪?
即使文件内容、扩展名、MIME 全部过关,如果上传后的文件落在 Web 可访问路径(如 /uploads/)且该目录允许执行脚本,那前面所有校验都形同虚设。常见错误是把上传目录放在 DocumentRoot 下,又没在服务器配置里禁用 .php 解析。
- Apache 需加
<directory> php_flag engine off </directory> - NGINX 应配
location ^~ /uploads/ { deny all; }或确保该路径不进fastcgi_pass流程 - 最佳实践是上传到 Web 根目录外(如
/data/uploads/),通过代理或后端读取,而非直接 URL 访问 - Linux 下上传目录权限别设成
777,755足够,且属主应为运行 Web 进程的用户(如www-data)
最容易被忽略的点:很多团队花大力气写文件头检测、扩展名白名单,却忘了检查 move_uploaded_file() 的目标路径是否可控。如果攻击者能通过 ../ 或空字节截断(%00)把文件写到 /var/www/html/ 下,前面所有防御都会被绕过。校验和存储必须视为原子操作,路径拼接前必须做标准化(realpath())、白名单目录约束、禁止相对路径符号。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











