accept属性仅影响文件选择对话框筛选,无法阻止恶意文件上传;真正可靠的前端过滤是校验file.name扩展名,但必须转小写并严谨提取后缀;file.type和accept均不可信,所有校验必须在服务端重做。

accept 属性根本拦不住恶意脚本文件
它不校验、不拦截、不报错,只影响文件选择对话框的默认筛选。用户点一下「所有文件」,或者直接拖拽一个 hack.js 进来,input.files 里照样有这个文件,submit 照样发出去。
常见错误现象:
- 写了
accept=".pdf,.docx",结果上传了shell.php或payload.html - 用 JS 检查
file.type === "text/html",但该字段为空或被浏览器误判为"text/plain" - 移动端 Safari 完全忽略
accept="application/javascript",连提示都不显示
真正能靠得住的前端过滤只有 file.name 扩展名比对
file.name 是用户选中时的真实文件名,包含后缀,是目前最稳定、最可依赖的前端判断依据。但必须注意大小写和多点分隔(如 archive.tar.gz)。
实操建议:
- 统一转小写再比对:
filename.toLowerCase().endsWith('.js') - 提取扩展名要严谨:
const ext = filename.slice(filename.lastIndexOf('.')).toLowerCase(),避免被data.tar.gz这类双后缀误导 - 白名单硬编码,别用黑名单——
['.js', '.html', '.php', '.exe']全部拒绝,而不是只放行['.pdf', '.xlsx'] - 提交前遍历
input.files,逐个检查,发现非法文件立刻preventDefault()并提示具体文件名
为什么不能信 file.type 和浏览器 Content-Type
file.type 是浏览器根据文件头或扩展名推测的 MIME,不可控、不可信。尤其对脚本类文件,几乎总是返回空字符串或 "text/plain";而服务端收到的 req.headers['content-type'] 或 PHP 的 $_FILES['file']['type'] 更是完全由客户端构造,改个请求头就能绕过所有前端逻辑。
PigX UI Pro 前端开发指南 - Vue 3 + TypeScript + Element Plus。当用户提到 PigX UI、PigX 前端、lgb-mgui 项目、Vue 3 企业级后台开发、Element Plus 后台开发时使用此技能。
典型陷阱:
- 把
malware.exe改名为report.pdf→file.type变成"application/pdf",但实际是 PE 文件 - 上传
index.html,Chrome 报"text/html",Firefox 可能报"text/plain",Safari 直接为空 - 用
fetch+FormData手动构造上传时,file.type字段可任意伪造
accept 唯一有用的地方:减少误操作,不是防攻击
它唯一靠谱的价值,是降低普通用户选错文件的概率,比如让财务人员在上传凭证时,默认只看到 .pdf 和 .jpg,而不是满屏的 .tmp、.log、.exe。但这层“友好”必须配合后端真实校验才成立。
配置要点:
- 混合写法最稳:
accept=".pdf,application/pdf,.xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" - 逗号后绝对不能有空格:
accept=".js,.html"✅,accept=".js , .html"❌(旧版 Safari 失效) - 表单必须带
enctype="multipart/form-data",否则后端收不到文件字段,连校验机会都没有 - 哪怕前端做了所有检查,也要假设请求来自 curl 或 Postman —— 所有校验逻辑必须在服务端重做一遍
最容易被忽略的是:你写的每一条前端过滤逻辑,在攻击者眼里都只是参考文档。真正的防线永远在服务端读取文件头(magic bytes)、解析真实 MIME、比对扩展名与内容一致性,并剥离原始文件名重命名存储。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










