go mod verify 是唯一能直接确认依赖是否被篡改的命令,但需配合 go.sum 和 GOSUMDB 才有效;失败时应先检查 go.sum 是否被修改、恢复后重拉依赖,而非手动删除;CI 中须强制校验下载、高危模块引入必要性及 go.sum 提交状态。

go mod verify 是唯一能直接确认依赖是否被篡改的命令,但它不会自动修复或下载,必须配合 go.sum 和校验源协同使用。只跑一次、只在本地验证、跳过 CI,等于没做。
go mod verify 失败时先别删 go.sum
输出 some modules failed verification 或 checksum mismatch 时,常见错误是直接手动删掉 go.sum 里报错的那一行。这会让后续构建失去校验依据,反而放大风险。
- 先用
git status go.sum看它是否被意外修改过;如果是,git checkout go.sum恢复原始记录 - 再运行
go mod download强制重拉依赖(不走缓存),之后立刻go mod verify - 若仍失败,检查
GOSUMDB是否被设为off或私有地址——应强制设为sum.golang.org,尤其在 CI 中加GOSUMDB=sum.golang.org环境变量 - 失败模块若来自私有仓库,需确认其代理是否支持 sumdb 查询;不支持的要手动比对
sum.golang.org上对应条目
go.sum 里藏着两个哈希,缺一不可
go.sum 每个模块实际存两条哈希:h1:xxx(zip 包内容)和 go.mod h1:yyy(模块元信息)。篡改者常只替换源码但漏改 go.mod 哈希,导致 go mod verify 报错但提示不明确。
- 报错中若含
go.mod h1:不匹配,说明模块根目录下的go.mod文件被改写过(比如注入恶意replace) - 可用
go list -m -json <module>@<version>查该模块真实go.mod内容,再用go mod hash(需 Go 1.22+)或 Python 脚本计算 dirhash 对比 - 不要信任
go mod graph显示的路径——它不校验哈希,只画依赖关系;恶意包可能伪装成合法路径
CI 流水线里必须验证三件事
单靠 go mod verify 不足以覆盖篡改场景。CI 中至少要卡住以下三个点:
- 执行
go mod verify前,先跑go mod download,确保所有依赖已落地且未被缓存污染 - 用
go list -m all | grep -E "(golang\.org/x|github\.com/.*\.(crypto|http|sql))"提取高危路径模块,再对每个模块运行go mod why <module>,确认引入必要性(避免 test-only 包混入构建) - 校验
go.sum文件本身是否被提交——如果 CI 报missing go.sum entry,不是工具问题,是go.sum没进 Git,得立刻补上
真正难防的是那种只改一行 init() 函数、不触发任何静态扫描规则、也不改变哈希值的篡改。这时候 go mod verify 会通过,但行为已变。所以最终防线不在工具链,而在:谁有权限往 go.mod 里加 replace?谁批准了那个 v0.0.0-20260801... 的伪版本?这些人工卡点,比任何命令都关键。

















