accept属性仅影响文件选择框可见项,无法真正限制上传;真正有效的校验必须依赖后端对文件头(magic bytes)的检测,前端所有限制均可被绕过。

accept 属性根本拦不住非法文件,它只影响文件选择框的默认可见项;真正能限制上传的,只有后端对文件头(magic bytes)的校验。
accept 属性写法必须 MIME + 扩展名双写,且逗号后不能有空格
单写 accept=".pdf" 或 accept="application/pdf" 都容易失效:iOS Safari 依赖 MIME,安卓 WebView 常忽略 MIME 只认扩展名,Windows 还可能上报非标准 MIME(如 application/x-pdf)。
-
accept="application/pdf,.pdf"✅ 覆盖主流平台 -
accept="image/png,image/jpeg,.png,.jpg,.jpeg"✅ 显式列出变体,避免漏掉image/pjpeg -
accept="application/vnd.openxmlformats-officedocument.wordprocessingml.document,.docx"✅ .docx 的唯一可靠组合 -
accept="text/csv,.csv"✅ 不要用text/*,它会匹配text/html,埋下 XSS 风险 -
accept="image/png , .jpg"❌ 逗号后有空格 → 旧版 Safari 直接丢弃整个属性
前端 JS 必须遍历 files 并校验 name 和 magic bytes
event.target.files 是原始列表,accept 完全没过滤它。用户拖拽、点“所有文件”、改后缀名(malware.exe → report.jpg),file.type 就不可信——它为空、可伪造、依赖扩展名或浏览器猜测。
- 用
file.name.toLowerCase().endsWith('.pdf')或正则file.name.match(/\.(pdf|docx)$/i)判断扩展名 - 关键业务必须加 magic bytes 校验:用
FileReader读前 4 字节,PDF 是%PDF(25 50 44 46),XLSX 是PK\x03\x04 - 设了
multiple后,files里可能混着合法与非法文件,必须逐项检查,不能只看files[0] - 提示要具体:
"已选 2 个不支持的文件(scan.tif,data.zip)",别只说“类型错误”
后端必须以文件头为唯一可信依据,扩展名和请求头都可伪造
前端所有限制都可被绕过:curl、Postman、DevTools 改 DOM、拖拽 Drop 区域……服务端收到的 Content-Type 请求头、req.file.mimetype(Node.js)、$_FILES['file']['type'](PHP)全部不可信。
立即学习“前端免费学习笔记(深入)”;
- 必须读取文件二进制流前几个字节(magic bytes):
PDF开头必须是%PDF,PNG是\x89PNG,XLSX是PK\x03\x04 - Node.js 推荐用
file-type库(读 buffer 前 12 字节),别手动比对 - PHP 用
finfo_file($finfo, $tmpFile),禁用$_FILES['file']['type'] - Java Spring 禁用
MultipartFile.getContentType(),改用Tika或Files.probeContentType()
form 提交时 enctype 忘设会导致后端收不到文件
如果 <form> 没加 enctype="multipart/form-data",哪怕 <input type="file"> 已选文件,后端也收不到 —— 浏览器会把文件字段当空字符串处理或直接忽略。
- 错误现象:
req.file is undefined(Express + multer),但控制台无报错,容易误判为前端 JS 问题 - 必须显式设置:
<form action="/upload" method="POST" enctype="multipart/form-data"> - 用
fetch+FormData时,不要手动设headers: {'Content-Type': 'multipart/form-data'},否则 boundary 会出错
最常被忽略的是:移动端 WebView(华为 EMUI、小米 MIUI)常直接忽略 accept,而 iOS Safari 可能因太严导致相册入口不可用;所以任何基于 accept 或 file.type 的判断,都不该出现在安全逻辑路径里。



















