必须读取文件头4字节(0x50, 0x4b, 0x03, 0x04)才能可靠判断zip文件,因后缀名和mime类型均可伪造;前端校验仅为体验优化,服务端才是最终裁判。

怎么用 JavaScript 快速判断用户选的文件是不是真正的 ZIP 文件
仅靠 file.name.endsWith('.zip') 或 file.type === 'application/zip' 完全不可靠。用户把 shell.php 改名为 report.zip,file.type 就可能为空或返回 application/octet-stream;安卓 WebView 下 file.type 经常失效;后缀名大小写(.ZIP、.Zip)也会让简单字符串匹配漏判。
实操建议:
- 先检查
file.type是否为application/zip或以application/x-zip开头——这是最快路径,但必须容忍为空 - 若
file.type为空或不匹配,退而匹配文件名后缀:file.name.toLowerCase().match(/\.(zip|jar|epub|docx|xlsx|pptx)$/),注意覆盖常见 ZIP 衍生格式 - 避免用
file.name.substring(file.name.lastIndexOf('.'))提取后缀——它在my.archive.zip.bak或无后缀文件上会出错 - 不要只校验扩展名,更不要信任
accept=".zip":它只影响系统对话框过滤,用户点“所有文件”就能绕过
为什么必须读取文件头(Magic Bytes)才能确认 ZIP 合法性
真正的 ZIP 文件开头 4 字节固定为 50 4B 03 04(十六进制),这是 ZIP 格式的“魔术字节”。后缀或 MIME 类型都能伪造,但改这 4 字节会导致文件无法解压。浏览器可通过 FileReader 读取前若干字节做比对,这是客户端最接近服务端校验的手段。
实操建议:
- 在
change事件中立即创建FileReader,调用readAsArrayBuffer(file.slice(0, 4)) - 在
onload回调里将结果转为Uint8Array,比对前 4 字节是否等于[0x50, 0x4B, 0x03, 0x04] - 注意:
file.slice()不复制内存,开销极小;但不要读整个文件,只读前 4–8 字节足矣 - 如果用户选的是空文件(
file.size === 0),直接拒绝,避免readAsArrayBuffer报错
上传前压缩包内容预检能防什么,不能防什么
前端读取 ZIP 头只能确认“它长得像 ZIP”,但无法知道里面有没有恶意文件(如 webshell.php)、是否包含目录遍历路径(../etc/passwd)、或是否被加了密码。这些必须由服务端解压扫描完成。
实操建议:
- 可选做 ZIP 内容结构快速探查:用
JSZip.loadAsync(file)(需引入 JSZip 库)尝试解析目录结构,捕获Corrupted zip or bug: unexpected signature类错误 - 但别在前端解压全部内容——性能差、内存爆、且无法替代服务端病毒扫描
- 若检测到 ZIP 内含可执行文件(
.exe、.bat、.php等),可提前提示用户“该压缩包含潜在风险文件”,但不阻止上传(权限判断是后端的事) - 永远不要相信前端传来的
filename或内部路径名;服务端接收后必须重命名 + 白名单过滤 + 解压时校验路径安全性
服务端才是 ZIP 合法性的最终裁判
前端所有校验都只是体验优化和初步过滤。攻击者禁用 JS、用 Postman 构造请求、或直接调用上传接口,都能绕过一切前端逻辑。真正决定“这个 ZIP 能不能存、能不能解、能不能执行”的,只有服务端。
关键点:
- PHP 中用
fopen($tmp_file, 'rb')读前 4 字节比对魔术字节,比$_FILES['file']['type']可靠 100 倍 - 解压时必须使用安全库(如 PHP 的
ZipArchive::open()配合ZipArchive::getFromName()),禁止用system('unzip')这类 shell 调用 - 检查 ZIP 内每个文件路径是否含
..、是否以/开头、是否超出目标目录范围 - 对解压出的每个文件,再走一遍类型校验(比如图片就再验一次文件头,脚本就拒绝)
最容易被忽略的是:前端校验通过后,用户仍可能在两次请求之间篡改文件(比如用调试器替换 input.files[0])。所以服务端不仅要做“是不是 ZIP”,还要做“是不是同一个 ZIP”——比如计算上传文件的 SHA256 并与前端预签名比对(需配合后端签发临时 token)。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











