accept属性仅提示文件类型,无法阻止用户选择不匹配文件;change事件中必须二次校验文件后缀、大小及魔数。

accept 属性只能提示,不能阻止选错
accept 不是校验开关,它只影响文件选择对话框的默认过滤视图。用户点开后手动切到“所有文件”,就能选任意后缀——比如你写 accept=".html,.htm",照样能选 evil.js 或 report.pdf。浏览器不会拦截、不报错、也不清空 files 列表。别把它当安全栅栏,它只是个礼貌的招呼。
change 事件里必须做二次检查
用户选完文件后,change 事件是前端唯一可控的拦截点。此时应立刻检查:
• 文件名后缀是否匹配(用 file.name.toLowerCase().endsWith('.html'),别信 file.type,它在 Windows 和 iOS 上常为空)
• 文件大小是否合理(比如 HTML 文件通常不超 10MB,if (file.size > 10 * 1024 * 1024) {...})
• 可选:读取前几百字节做 magic number 初筛(如开头含 或 <code>,但注意编码和 BOM)<br>检查失败就清空 <code>input.value = '' 并提示,否则后续上传会把错误文件发出去。
服务端必须校验真实 MIME 类型
前端所有检查都可被绕过,后端才是最终防线。关键点:
• 别依赖请求头里的 Content-Type 字段,它由客户端提供,可伪造
• 别只看文件扩展名,攻击者会把恶意脚本改名为 index.html
• 必须读取文件前若干字节(至少 512 字节),用 libmagic(Node.js 用 file-type,Python 用 python-magic)识别真实类型
• 白名单只允许 text/html,拒绝 application/javascript、text/plain 等看似接近实则危险的类型
• 校验通过后再重命名(如生成 UUID)、存到非 Web 可访问目录
iOS Safari 的 accept 基本不可信
在 iOS Safari(尤其旧版)上:
• accept=".html" 几乎无效,文件选择器仍显示所有文件
• accept="text/html" 更不可靠,部分版本直接忽略
• 用户从“文件”App 拖入或通过 AirDrop 接收的 HTML 文件,accept 完全不起作用
所以 iOS 场景下,change 事件里的后缀检查 + 后端 magic bytes 校验是唯二靠谱手段。如果业务强依赖 HTML 内容解析,还得在服务端打开 HTML 并提取 <script></script> 标签做进一步风险扫描。
真实限制不在前端界面,而在后端对字节流的诚实判断。accept 是门帘,change 是门卫,服务端才是锁死的保险柜——少一个环节,门就虚掩着。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











