防范sql注入的核心是坚持参数化查询:所有用户输入必须通过?或$1占位符传参,禁用字符串拼接;order by、表名、字段名等动态标识符须经白名单校验;gorm raw()仅占位符安全,null字段需用sql.null类型scan。

只要用 db.Query、db.Exec 或 GORM 的 Where/Raw 时坚持参数化传参,Gin 本身不引入额外注入风险——它只是个 HTTP 路由器,真正决定安全与否的是你调数据库那几行代码。
database/sql 原生查询必须用占位符,别拼字符串
很多人以为 Gin 框架自带防护,其实它完全不管 SQL 怎么写。只要你写了 "SELECT * FROM users WHERE name = '" + name + "'",哪怕用 Gin 接收参数,也等于把数据库钥匙交出去了。
- MySQL 驱动只认
?:比如db.Query("SELECT id FROM users WHERE status = ? AND age > ?", "active", 18) - PostgreSQL 驱动只认
$1、$2:写成?会直接报pq: syntax error at or near "?" - 混用占位符和字符串拼接是高频翻车点:比如
"WHERE name = ? ORDER BY " + sortField—— 后半段sortField就裸奔了 - 错误现象常被误判:查不到数据不是因为没匹配,而是参数根本没进占位符,被当字面量处理了,比如
sql: no rows in result set实际是 SQL 执行失败
GORM 的 Where 和 Raw 方法怎么写才安全
db.Where 看似方便,但写错就掉坑里。它支持参数化,但不支持动态字段名或表名;Raw 更危险,它绕过 GORM 大部分校验,全靠你自己把关。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- ✅ 安全写法:
db.Where("name = ? AND deleted = ?", name, false).Find(&users),所有值走? - ❌ 危险写法:
db.Where("name = '" + name + "'").Find(&users),字符串拼接直接失效 -
IN子句要传 slice,不能拆开:db.Where("id IN ?", []uint{1,2,3})才对;写成"id IN (?, ?, ?)"再传三个参数,既啰嗦又易错 -
Raw必须带占位符:db.Raw("SELECT * FROM ? WHERE score > ?", tableName, minScore)是错的——tableName不能用?;正确做法是白名单映射:if tableName == "users" { db.Raw("SELECT * FROM users WHERE score > ?", minScore) }
ORDER BY、表名、字段名这些“不能参数化”的地方怎么防
SQL 标准不允许把表名、列名、排序方向做成参数,所有驱动都会拒绝 ORDER BY ? 这种写法。这时候拼接不是选项,是陷阱;白名单才是唯一靠谱路径。
- 排序字段必须硬约束:
validSortFields := map[string]bool{"created_at": true, "score": true, "name": true},查之前先判断if !validSortFields[sort] { c.AbortWithStatus(400); return } - 表名映射建议用常量或 switch:
switch inputTable { case "prod": table = "orders_prod"; case "test": table = "orders_test" },别用"orders_" + inputTable - 排序方向(
ASC/DESC)同样要白名单:if dir != "ASC" && dir != "DESC" { /* 拒绝 */ } - 别信“转义函数”:
strings.ReplaceAll(col, "'", "''")对admin' OR '1'='1这类结构型攻击完全无效
泛型 GORM 和 GEN 能提前掐断哪些问题
传统 GORM 是泛型擦除的,db.First(&user) 却查 Order 表也不会编译报错;而泛型版 gorm.G[User](db) 把模型和操作强绑定,类型错直接编译失败——这对字段名拼错、表名写反这类低级但高频的问题特别有用。
- 必须带
context:gorm.G[User](db).Where("name = ?", name).First(ctx, &user),否则超时控制失效,且 GEN 默认启用WithContext,忘传会 panic - GEN 把注释里的 SQL 模板(如
// SELECT * FROM users WHERE name = {{.name}})编译成类型安全方法,连字段名写成user_nam都会在编译时报错 - GEN 生成的代码底层仍走
Query/Exec,但强制所有参数走绑定,杜绝手写Raw的机会;不过如果你绕过生成代码、直接调db.Raw(),防护就归零 - 注意兼容性:
gorm.G[T]要求 GORM >= v1.30.0,升级后所有First/Find调用都得补全ctx和类型参数,漏一个就编译不过
最常被忽略的不是语法层面的拼接,而是业务逻辑里该校验却没校验的字段——比如前端传个 status=999,后端没做枚举校验就直接塞进 WHERE status = ?,虽然不导致注入,但可能查出不该查的数据,甚至触发越权行为。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










