JSON接口本身不执行SQL,但解析后直接拼接字段值进SQL语句(如WHERE name = '+name+')即引发SQL注入;必须全程使用参数化查询,所有JSON解析结果均视为不可信输入,禁止字符串拼接,动态字段名需白名单校验。

JSON API 本身不执行 SQL,但一旦把解析后的 JSON 字段直接拼进 SQL 查询,风险就立刻转移过去——关键不在 JSON 格式,而在你用它干了什么。
json.Unmarshal 后直接插进 SQL 是高危操作
很多开发者误以为“用了 JSON 就安全”,结果在 json.Unmarshal 解出 map[string]interface{} 或结构体后,把其中的 user_input 字段原样塞进 fmt.Sprintf("SELECT * FROM users WHERE name = '%s'", name),这就等于亲手把注入入口焊死在代码里。
- JSON 只是数据容器,
json.Unmarshal不做任何校验,恶意字符串如"admin' OR '1'='1"会被完整还原为 Go 字符串 - 后续若用字符串拼接构造 SQL(哪怕只用于日志或调试打印),都可能被下游模块误用或重构时暴露
- 尤其危险的是动态字段名场景:比如前端传
{"field": "username", "value": "admin'--"},后端再拼成WHERE %s = '%s',双杀
类型校验不能替代参数化查询
仅靠 if v, ok := data["id"].(float64) 或 strconv.Atoi() 做类型断言,只能防住非数字输入,但对字符串类字段(name、email、search_term)完全无效。攻击者本就不需要改类型,只要改内容。
- 字符串字段必须走白名单校验:比如邮箱用
mail.ParseAddress,用户名用正则^[a-zA-Z0-9_]{2,20}$,而非简单len() > 0 - 对允许特殊字符的字段(如评论内容),校验 ≠ 放行——校验后仍须进参数化查询,不能因为“已校验”就放松拼接
- 注意 JSON 解析的隐式类型转换:
"123"解为float64,"true"解为bool,但"1'; DROP TABLE--"仍稳稳落在string类型里,校验器根本拦不住
真正有效的防护链条只有这一条
从接收到落库,每个环节都要明确“这一步是否信任该值可直接执行”。答案永远是否定的,除非它来自硬编码常量或经由可信上下文生成。
- 接收 JSON:用
json.RawMessage延迟解析,避免过早反序列化不可信字段 - 提取字段:用结构体 +
json:"name,string"标签强制转字符串,防止数字/布尔被意外当字符串用 - 进 SQL:所有变量必须走
db.Query("SELECT * FROM t WHERE name = ?", name),绝不用+或fmt.Sprintf - 例外情况(如动态表名):必须严格白名单匹配,且禁止任何形式的用户控制,宁可报错也不 fallback 到拼接
最常被忽略的一点:日志语句里写 log.Printf("query for %s", userInput) 看似无害,但如果日志系统支持模板渲染或被 ELK 类工具二次解析,就可能触发 SSTI 或命令注入——JSON 字段的安全边界,从来不止于数据库那一层。


















