go的zip.writer默认仅归档不压缩,必须显式设置header.method = zip.deflate启用deflate压缩;createheader需校验路径防zip slip,中文名须设flags|=0x800,close()不可忽略否则zip损坏。

zip.Writer 默认不压缩,不是“调用就压”,必须显式设 header.Method = zip.Deflate,否则生成的是纯归档(zip.Store),体积几乎不变。
zip.Writer.CreateHeader 的路径校验失败静默丢弃
zip.Writer.CreateHeader 对 header.Name 做严格校验:含 ../、以 / 开头、含 Windows 非法字符(如 :、?、*)时直接返回 error,但不会 panic,也不会中断后续流程。
结果是 ZIP 文件看似生成成功,内部条目缺失,unzip -t 报 “invalid central directory”,Windows 资源管理器打不开。
- 必须对每个
header.Name调用前做预处理:filepath.ToSlash(filepath.Clean(relPath)) - 用户上传的原始文件名绝不能直传,需白名单过滤(例如只允许
[a-zA-Z0-9.<em>-]+(?:/[a-zA-Z0-9.</em>-]+)*) - 每次
w.CreateHeader(header)后必须检查err != nil,否则后续写入无效
解压时不做路径净化等于开放任意写入
f.Name 是 ZIP 中声明的路径,攻击者可构造 ../../../etc/passwd 类似条目。若解压时不校验,直接 os.OpenFile(filepath.Join(dst, f.Name), ...),就会覆盖系统关键文件。
- 先用
filepath.Clean(f.Name)归一化路径(消除..、.、重复/) - 再判断是否仍含
".."或以"/"开头,有则跳过 - 更稳妥做法:算出目标根目录绝对路径
dstAbs, _ := filepath.Abs(dstDir),再检查filepath.Join(dstAbs, cleanedName)是否仍以dstAbs开头(用strings.HasPrefix+filepath.ToSlash统一格式) - 遇到
f.IsDir()为true,必须先os.MkdirAll(..., 0755),不能依赖自动创建
中文文件名在 Windows 上乱码的根源与修复
Go 默认按 UTF-8 写文件名,但旧版 Windows 资源管理器(Win7 / Win10 早期)默认用 GBK 解码,导致中文显示成 ???.txt。
- 关键修复:设置
header.Flags |= 0x800,表示文件名使用 UTF-8 编码 - 这个 flag 必须在
CreateHeader前设置,且仅对 UTF-8 字符串有效 - 不要依赖
filepath.Base直接截取用户输入——它不处理编码,也不清理非法字符
zip.Writer.Close() 不只是收尾动作,它负责写入中央目录;漏掉或忽略其返回的 error,会导致 ZIP 可见文件名但无法解压内容——这是最常被忽略、也最难排查的损坏点。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











