go中json.unmarshal必须传指针,否则静默失败;正确用法是传&v并检查error,结构体字段需导出且用指针接收者实现unmarshaljson,配合json.rawmessage白名单校验可防注入攻击。

直接用 json.Unmarshal 解析不受信任的 JSON 是高危操作,不加校验就进业务逻辑,等于给攻击者开了内存读写、DoS 甚至 RCE 的后门。
为什么 UnmarshalJSON 方法必须用指针接收者
写了 UnmarshalJSON 却没生效?八成是接收者类型错了。Go 的 json 包只认指针接收者,值接收者会被静默忽略——不报错、不调用、字段照旧被默认反序列化,校验形同虚设。
-
func (s MyStruct) UnmarshalJSON([]byte) error❌ 不触发 -
func (s *MyStruct) UnmarshalJSON([]byte) error✅ 唯一有效写法 - 若结构体含嵌套指针字段(如
*time.Time),在方法内手动解时要先判空,避免 panic - 别在
UnmarshalJSON里直接调json.Unmarshal(s, data),会递归调自己导致栈溢出
如何用 json.RawMessage 拦截并白名单校验字段
攻击者常靠未知字段(如 "__proto__"、"constructor")触发原型污染或覆盖内部状态。DisallowUnknownFields() 能挡一部分,但无法防键名合法、值恶意的情况(比如 "price": -999999999999)。真正可控的方式是先“懒加载”再校验。
- 定义结构体字段为
map[string]json.RawMessage,把原始字节先存住 - 遍历 key,用
slice.Contains(whitelist, key)检查是否在预设白名单中(如[]string{"name", "email", "role"}) - 对每个白名单 key 对应的
json.RawMessage,单独调json.Unmarshal到临时变量,并做类型/范围校验(如role只能是"admin"或"user") - 遇到非法 key 直接返回
fmt.Errorf("unknown field: %q", key),不继续解析
数字字段类型混淆怎么防:int64 接收 float64 报错
前端传 {"value": 12.34},结构体字段却是 Value int64 `json:"value"`,json.Unmarshal 会直接 panic:json: cannot unmarshal number 12.34 into Go struct field X.value of type int64。这不是校验失败,是解析器词法层崩了,错误信息也不带上下文。
- 不要指望
omitempty或标签选项解决——它只控制字段存在性,不改变类型约束 - 正确做法:为该字段定义新类型(如
type Score int64),实现UnmarshalJSON方法 - 在方法里先用
json.Unmarshal(data, &f)解到float64临时变量,再判断是否为整数、是否越界 - 非法值返回明确错误(如
"score must be integer between 0 and 100"),而不是让 panic 泄露服务细节
严格模式 + 自定义校验 + 白名单,三者缺一不可
只开 DisallowUnknownFields() 挡不住字段值层面的攻击;只做自定义 UnmarshalJSON 但没白名单,可能被绕过嵌套结构体或接口字段;只做白名单却不校验值,等于放行恶意数字或超长字符串。真实攻击往往组合出手——比如先塞一个超深嵌套耗尽内存,再混一个 "role": "admin" 突破权限校验。关键不是堆砌防护,而是让每层校验都落在攻击链的必经之路上。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











