拼接WHERE条件时不能用fmt.Sprintf,因其会将用户输入直接拼入SQL导致注入;应使用占位符传值、白名单校验字段名和操作符,并用conds/params切片安全组装。

拼接WHERE条件时为什么不能用 fmt.Sprintf
因为 fmt.Sprintf 拼出来的字符串会直接进入 SQL 解析器,只要用户输入里带单引号、括号或注释符(比如 ' OR 1=1 --),就可能改变查询逻辑。database/sql 和 GORM 都不会帮你识别或转义这段字符串——它已经被当成合法 SQL 的一部分执行了。
常见错误现象:sql: no rows in result set 看似查不到数据,其实是拼错字段名或值被当字面量处理了;更糟的是返回了不该返回的全部记录,却没人察觉。
- 哪怕输入来自内部枚举,只要未来可能开放给前端或 API,就必须按用户输入对待
-
sqlx、gorm这些库不拦截你拼字符串,只保证你自己传进去的占位符被正确绑定 - gosec 规则
G201会标出所有Raw()调用,但不会标fmt.Sprintf("WHERE name = '%s'", input)—— 它看起来太“正常”了
动态 WHERE 条件该用什么结构安全组装
核心原则:**值走占位符,结构走白名单 + 显式拼接**。不能把字段名、操作符(LIKE、!=)、布尔逻辑(AND/OR)交给用户决定,但可以动态组合已校验过的条件块。
推荐做法是拆成两个切片:conds []string 存条件片段(如 "status = ?"、"name LIKE ?"),params []interface{} 存对应参数。最后用 strings.Join(conds, " AND ") 拼 SQL,再整体传参。
- 字段名必须白名单:比如只允许
map[string]bool{"name": true, "email": true, "status": true} - 操作符必须限定范围:
if op != "=" && op != "LIKE" && op != "IN" { return err },别信strings.ToUpper(op)后直接拼 - IN 查询别手动拼
?个数:用sqlx.In或db.Where("id IN ?", idSlice),让驱动自动展开
GORM 的 Where 方法哪些写法会悄悄破防
db.Where 默认安全,但一加字符串拼接就失效。GORM 不会解析你传进去的字符串里有没有 ?,它只管把整个字符串当 SQL 片段塞进去。
错误示例:db.Where("name = ? AND type IN (" + strings.Join(types, ",") + ")", name) —— types 若含恶意内容,直接注入。
- ✅ 安全:
db.Where("name = ?", name).Where("type IN ?", types)(types是[]string) - ❌ 危险:
db.Where(fmt.Sprintf("name = '%s'", name))(单引号+拼接) - ❌ 危险:
db.Where("name " + op + " ?", name)(op未白名单校验) - ⚠️ 注意:
db.Where("status = ? OR score > ?", status, score)参数顺序必须和?出现顺序严格一致
sqlx.NamedQuery 怎么用才不踩坑
sqlx.NamedQuery 支持命名占位符(如 :name、:min_score),比位置占位符更易读,但前提是传入的是 map[string]interface{} 或 struct,且 key 名必须和 SQL 中的命名完全匹配。
容易忽略的点:struct 字段必须打 db tag,否则 sqlx 找不到对应字段,静默填零值。
- ✅ 正确:
type Filter struct { Name string `db:"name"` MinScore int `db:"score"` },SQL 写WHERE name = :name AND score > :min_score - ❌ 错误:
Filter{Name: "admin", MinScore: 80}但没dbtag →:name绑定为空字符串 - ⚠️ 注意:
NamedQuery不解决标识符(表名、ORDER BY 字段)参数化问题,这些仍需白名单
实际拼 WHERE 最难的不是语法,而是判断哪部分算“值”、哪部分算“结构”。一个字段名看着像值,但它在 SQL 编译期就得确定,数据库根本不给你运行时替换的机会。这种直觉得练,而不是背规则。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











