go语言防sql注入须参数化传值(?或$1)禁字符串拼接,动态标识符如表名、字段名、排序方向等必须白名单校验,scan需处理null与类型匹配,gorm禁用raw拼接且确保preparestmt开启。

Go 语言本身不防 SQL 注入,database/sql 包也不自动转义或拦截恶意输入——它只提供安全接口,但是否安全,全看你有没有把用户输入塞进 SQL 字符串里。
用 Query 和 Exec 必须传参,不能拼字符串
MySQL 驱动认 ?,PostgreSQL 驱动认 $1、$2,这些占位符由驱动层做类型绑定和转义。只要你不手动拼接,攻击者就无法改变 SQL 结构。
- ✅ 正确:
db.Query("SELECT * FROM users WHERE id = ?", userID) - ❌ 危险:
db.Query("SELECT * FROM users WHERE id = " + userID)或fmt.Sprintf("... id = %s", userID) - ⚠️ 注意:
userID是string类型但实际是数字?多数驱动能隐式转换,但遇到NULL、time.Time、JSON 字段时可能 panic 或绕过校验
ORDER BY、TABLE NAME、LIMIT 不能参数化
SQL 标准规定这些属于“查询结构”,不是“值”,所以 db.Query("ORDER BY ?", "name") 会直接报错:sql: expected 0 arguments, got 1。这时候硬上参数化没用,得靠代码兜底。
- 白名单校验字段名:
validFields := map[string]bool{"name": true, "created_at": true, "score": true},查不到就拒绝 - 表名映射用常量或
switch,比如输入"prod"→ 实际查"users_prod",而不是"users_" + input - 排序方向(
ASC/DESC)也得白名单:if dir != "ASC" && dir != "DESC" { return err }
Scan 接收 NULL 和类型不匹配会 panic
防注入不只是“发出去”的事;接收端出问题可能暴露逻辑漏洞,甚至让攻击者通过错误响应反推表结构。
- 数据库字段允许
NULL?别用普通string接,改用*string或sql.NullString - 扫描到结构体时,字段顺序/别名必须和
SELECT列严格一致;SELECT id, name AS username要对应Username string,否则数据错位 - 用
sqlx.StructScan可按字段名匹配,但依然要处理NULL,且性能略低
GORM 的 Raw 不是免死金牌
GORM 默认查询是安全的,但一旦调用 db.Raw() 或 Session().WithContext() 后手动拼 SQL,防护就归零了。
- ❌ 危险:
db.Raw("SELECT * FROM articles WHERE boss_id = '" + c.Param("id") + "'") - ✅ 安全:
db.Raw("SELECT * FROM articles WHERE boss_id = ?", c.Param("id")) - ⚠️ 特别注意:GORM v2 默认开启
PrepareStmt,但若显式关掉(PrepareStmt: false),某些链式查询会退化为字符串拼接,需检查配置
最易被忽略的点是:动态标识符(如表名、字段名、排序方向)根本没有“安全参数化”这回事,只能靠白名单+显式映射,而且这个逻辑必须在 SQL 构建前完成,不能靠事后过滤。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











