accept 属性仅提示文件类型,不强制过滤;按钮触发的文件选择仍受其影响,但用户可通过“所有文件”、拖拽或修改 dom 绕过;前端校验需在 change 事件中通过扩展名和 magic bytes 双重验证,后端必须进行真实 mime 和内容校验。

accept 属性对按钮触发的文件选择根本不起过滤作用
它只影响原生 <input type="file"> 弹出的系统对话框,默认隐藏不匹配类型;但如果你用按钮 + click() 触发这个 input,accept 行为完全不变——不是“按钮能绕过”,而是“按钮根本没参与控制”。所有限制逻辑仍在 input 上,和谁点它无关。
为什么点了按钮还是能选到 .exe 或 .html
accept 不是过滤器,只是提示。用户在文件选择器里点“所有文件”、拖拽、或直接删掉 DOM 中的 accept 属性,都能绕过。iOS Safari 甚至可能因 accept 值不标准(比如只写 .pdf)而直接禁用相册入口,导致连图都选不了——这不是 bug,是浏览器实现差异。
-
accept="image/*"会放行.psd、.svg(后者可嵌脚本) -
accept=".xlsx"在 iOS Safari 上大概率失效,必须写成accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,.xlsx" - 逗号后有空格(如
accept="image/png , .jpg")会让旧版 Safari 直接丢弃整个属性
按钮触发时必须补的前端校验逻辑
用户点按钮 → 触发 input.click() → 用户选完 → change 事件触发 → 此时才真正有机会拦截。别信 file.type,它常为空或被伪造:
- 用
file.name.toLowerCase().endsWith('.pdf')或正则/\.(jpg|png|pdf)$/i做扩展名白名单比对 - 关键业务加 magic bytes 校验:读前 4 字节,
%PDF(PDF)、\x89PNG(PNG)、PK\x03\x04(ZIP/XLSX) - 设了
multiple就必须遍历Array.from(event.target.files)每一项,不能只看第一个 - 提示要具体:
"已选 1 个不支持的文件(report.exe)",而不是弹“类型错误”
后端才是唯一可信的防线
前端所有检查,包括按钮触发流程里的 JS 校验,都可被绕过:curl 直传、Postman 改 header、DevTools 清空 input 的 accept 后手动选——服务端收到的文件,req.file.mimetype(Node.js)、$_FILES['file']['type'](PHP)全是客户端伪造的,不可信。
- 必须读取文件二进制流前若干字节,用
file-type(Node.js)、python-magic(Python)等库解析真实 MIME - 验证扩展名是否与内容一致(防
hack.html改名成data.pdf) - 保存时剥离原始扩展名,由服务端根据检测结果附加安全后缀
- 上传目录禁止执行权限,静态资源走独立域名或 CDN
最容易被忽略的是表单漏设 enctype="multipart/form-data",会导致后端根本收不到文件字段,排查时容易卡在 JS 逻辑里——这个属性不是可选项。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











