go test -fuzz 能暴露 panic、无限循环等底层缺陷,但无法发现 SQL 注入等业务逻辑漏洞;Fuzz 函数须满足命名、文件位置、调用 f.Add()/f.Fuzz() 三条件;需避免非确定性操作与外部依赖;高质量种子比长时模糊更有效。

Go 原生 go test -fuzz 能直接暴露 panic、无限循环、整数溢出、空指针解引用(如未检查的 nil map 写入)、错误返回值被忽略后引发的级联失败——但对 SQL 注入、XSS、权限绕过这类业务逻辑漏洞无能为力,因为 fuzzer 不知道“哪个字段该插 payload”。
func FuzzXXX(f *testing.F) 的签名和位置有硬性约束
不是随便写个函数就能被 go test -fuzz 识别。必须满足三个条件:
- 函数名必须以
Fuzz开头,参数类型严格为*testing.F - 文件名必须是
*_test.go,且在被测包目录下(不能放在internal/或其他非测试路径) - 函数体内必须调用
f.Add()或f.Fuzz();只写f.Add("a")但漏掉f.Fuzz(...),实际不会启动模糊变异
f.Fuzz() 闭包里不能有不可控副作用
一旦引入外部依赖,fuzzer 就无法稳定复现 crash,也就没法最小化输入。常见踩坑点包括:
- 调用
time.Now()或rand.Int()—— 每次输入结果不同,fuzzer 认为“非确定性”,跳过记录 - 读取环境变量(
os.Getenv)、打开文件(os.Open)、发起 HTTP 请求 —— 状态不隔离,可能污染后续 case - 修改全局变量或共享 map/slice —— goroutine 间状态串扰,崩溃不可重现
- 使用
log.Print或fmt.Println—— 输出干扰 fuzz 日志流,部分版本会静默失败
种子(seed)质量比模糊时长更重要
go test -fuzz=FuzzParseJSON -fuzztime=1h 跑一小时,不如 10 个好种子跑 30 秒有效。关键在于覆盖“结构边界”:
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
立即学习“go语言免费学习笔记(深入)”;
- 空输入:
f.Add("")、f.Add("{}")、f.Add("[]") - 深度嵌套:
f.Add(strings.Repeat("{", 1000) + strings.Repeat("}", 1000)) - 超长字段:
f.Add(`{"key":"` + strings.Repeat("x", 1 - 特殊字符:
f.Add(`{"\u0000\u202E\uFEFF":"test"}`)(空字符、Unicode 控制符) - 数值极值:
f.Add(`{"n":` + strconv.FormatInt(math.MinInt64, 10) + `}`)
避免只塞 “valid JSON” —— fuzz 引擎对合法输入变异效率低,真正容易崩的是半截、错位、嵌套失控的畸形结构。
panic 不等于漏洞,但 recover 后继续用损坏状态就是
很多开发者加了 defer func() { recover() }() 就以为安全了。fuzzing 经常暴露两类问题:
-
recover()没包住真正出问题的 goroutine(比如子协程 panic 未捕获) -
recover()后返回了部分解析的中间状态(如已部分写入的 map),上层逻辑误用导致数据污染或逻辑跳变 - panic 发生在 defer 链里(如
defer json.NewEncoder(w).Encode(x)中 x 为 nil),recover 无法捕获
真正要检查的不是“有没有 panic”,而是“panic 后程序是否仍处于可预测、可清理的状态”。这点极易被忽略,却恰恰是 DoS 类漏洞的温床。

















