gorm struct传参给where时字段名作列名、值作参数绑定,自动展开为安全占位符形式,但零值字段被忽略,in查询需显式占位符,表名等标识符仍需白名单校验。

GORM 的 Struct 映射本身不防注入,但配合正确条件绑定才能真正切断拼接路径——结构体只是载体,关键在 Where 怎么调用。
Struct 传参给 Where 时,字段名被当列名、值被当参数
当你写 db.Where(&User{Name: name, Status: status}).First(&u),GORM 会自动展开为 WHERE name = ? AND status = ?,两个值都走参数绑定。这比手写字符串安全,但有隐含前提:
- Struct 字段必须有对应数据库列名(通过
gormtag 或默认命名规则映射),否则该字段被忽略,不会报错 - 如果
User.Status是空字符串或零值,GORM 默认跳过该条件(可配clause.Eq{Column: "status", Value: status}强制包含) - 不能混用:比如
db.Where(&User{Name: name}).Where("age > " + ageStr),后半段立刻失效
IN 查询别直接传 slice 给 Struct,要用显式占位符
Struct 不支持 IN 语义的自动展开。写 db.Where(&User{ID: []uint{1,2,3}}) 不会生成 ID IN (?, ?, ?),而是静默忽略该字段。
- ✅ 正确做法:
db.Where("id IN ?", []uint{1,2,3}).Find(&users),GORM 自动展开为三个? - ❌ 错误写法:
db.Where("id IN (" + strings.Join(ids, ",") + ")"),哪怕 ids 全是数字也危险 - ⚠️ 注意:PostgreSQL 用
$1, $2, $3,MySQL 用?, ?, ?,驱动自动适配,不用手动改占位符格式
Preload 关联查询时,Struct 不影响主表 WHERE 安全性
Preload 只控制 JOIN 或子查询的加载逻辑,不影响主查询的 WHERE 构造方式。它本身不引入注入风险,但容易让人误以为“用了 Struct 就万事大吉”。
- ✅ 安全链路:
db.Preload("Orders").Where("name = ?", name).Find(&users) - ❌ 高危组合:
db.Preload("Orders").Where("name = '" + name + "'").Find(&users),Preload 再安全也救不了拼接的 WHERE - 关联字段名若来自用户输入(如动态 Preload("Profile."+field)),field 必须白名单校验,因为列名无法参数化
Struct Scan 接收结果时,字段顺序错配比注入更隐蔽
Scan 和 Find 无关,但它依赖 SELECT 的**列顺序**。一旦 SQL 改了字段顺序,Struct 字段就会错位,比如把 email 值扫进 ID uint 字段,触发类型 panic。
- 永远显式写 SELECT 字段:
SELECT id, name, email FROM users,不用SELECT * - 接收用 Struct 而非多个
&id, &name, &email,Struct 字段顺序必须和 SELECT 严格一致 - 可能为 NULL 的字段,Struct 中必须用
sql.NullString等类型,否则Scan会 panic
最常被忽略的一点:Struct 映射只解决“值怎么传”,不解决“结构怎么拼”。表名、排序字段、GROUP BY 表达式这些标识符,无论你传什么 Struct 都无法参数化,它们必须靠白名单硬约束——这才是真正容易崩盘的地方。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











