accept属性仅优化前端文件选择体验,无法阻止非法上传;必须服务端校验文件头魔数(如%pdf、\x89png)并结合白名单扩展名,且上传路径禁执行、文件名强制重命名。

上传前用 accept 限制文件类型,但别信它能防住非法文件
accept 只是浏览器 UI 层的提示,用户改个后缀或用开发者工具绕过轻而易举。它能过滤掉大部分误选,但不能替代服务端校验。常见写法:<input type="file" accept=".pdf,.docx,image/*">。注意:image/* 在 Safari 中可能不识别 webp,iOS 上对 video/* 支持也不稳定;如果必须限定 PDF,写 accept=".pdf" 比 accept="application/pdf" 兼容性更好。
用 File.size 和 File.type 做前端快速拦截
JavaScript 可以在用户选中文件后立刻检查大小和 MIME 类型,避免无效提交。但 File.type 是浏览器根据扩展名或魔数推测的,不可靠(比如把 .exe 改成 .jpg,type 就可能变成 image/jpeg)。实操建议:
- 用
file.size > 10 * 1024 * 1024拦截超大文件(如限制 10MB),比等上传完再报错体验好得多 - 对关键类型(如头像),用
file.type.startsWith("image/")+file.name.endsWith(".png")双重判断,降低误判率 - 不要用
file.type === "application/msword"判断 .doc——现代 Word 文档实际是application/vnd.openxmlformats-officedocument.wordprocessingml.document
上传时加 FormData 并监听 upload.onprogress 防假死
直接 fetch 或 XMLHttpRequest 提交 FormData 是标准做法,但容易忽略两点:一是没设请求头(fetch 会自动设置 Content-Type 为 multipart/form-data + boundary,手动设反而出错);二是没处理上传进度,大文件卡住没反馈。正确姿势:
const formData = new FormData();
formData.append("file", fileInput.files[0]);
fetch("/upload", {
method: "POST",
body: formData // 不要加 headers: { "Content-Type": ... }
}).then(r => r.json());
如需进度条,必须用 XMLHttpRequest:xhr.upload.onprogress = e => { if (e.lengthComputable) console.log(e.loaded / e.total); }。原生 fetch 目前不支持上传进度监听。
后端校验必须独立做,且不能只看 Content-Type 头
所有前端验证都可被跳过,后端才是最后一道门。重点不是“怎么读文件”,而是“怎么拒绝危险文件”:
- 不要信任
req.headers["content-type"]或file.mimetype(Express Multer 的字段),攻击者可伪造 - 必须读取文件前几个字节(即魔数):PDF 以
%PDF开头,PNG 是\x89PNG,ZIP(含 .docx/.xlsx)是PK\x03\x04 - Node.js 可用
file-type库解析魔数,Python 用python-magic,PHP 用finfo_file,但都要配合白名单(比如只允许["pdf", "png", "jpeg", "xlsx"]) - 上传路径务必禁止执行权限,文件名强制重命名(如 UUID + 白名单扩展名),绝不能保留原始
filename
魔数校验这步最容易被跳过——很多人以为后端检查了扩展名就安全了,其实只要文件开头字节匹配,Web 服务器就可能把它当真实类型处理,导致 XSS 或 RCE。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











