GORM SQL 注入防护
GORM 默认使用参数化查询,但不当使用 Raw() 和字符串拼接仍会引入 SQL 注入风险,需严格遵循安全查询规范。
SQL 注入原理
注入攻击示例
Go
// 危险示例:字符串拼接
userID := "1 OR 1=1"
db.Where("id = " + userID).Find(&users)
// 生成 SQL: SELECT * FROM users WHERE id = 1 OR 1=1
// 结果:返回全部数据
注意: 任何将用户输入直接拼接到 SQL 语句的行为都可能引入注入漏洞。
参数化查询
安全的 Where 条件
Go
// 正确:参数化查询
db.Where("id = ?", userID).Find(&users)
// 生成 SQL: SELECT * FROM users WHERE id = ? (参数绑定)
// 多个参数
db.Where("name = ? AND age > ?", name, age).Find(&users)
// IN 查询
db.Where("id IN ?", []int{1, 2, 3}).Find(&users)
Map 条件
Go
// Map 参数化
db.Where(map[string]interface{}{
"name": "张三",
"status": "active",
}).Find(&users)
// 生成 SQL: SELECT * FROM users WHERE name = ? AND status = ?
Struct 条件
Go
// Struct 参数化(仅非零值字段生效)
db.Where(&User{Name: "张三", Status: "active"}).Find(&users)
Raw SQL 安全使用
参数化 Raw 查询
Go
// 正确:使用参数绑定
db.Raw("SELECT * FROM users WHERE id = ? AND status = ?", userID, status).Scan(&users)
// 错误:拼接 SQL
db.Raw("SELECT * FROM users WHERE id = " + userID).Scan(&users) // 危险
Named SQL
Go
// 命名参数
db.Raw(
"SELECT * FROM users WHERE name = @name AND age > @age",
sql.Named("name", name),
sql.Named("age", age),
).Scan(&users)
动态排序字段防护
白名单校验
Go
var allowedSortFields = map[string]bool{
"name": true,
"created_at": true,
"age": true,
}
func SafeOrder(db *gorm.DB, sortField string, asc bool) *gorm.DB {
if !allowedSortFields[sortField] {
sortField = "created_at" // 默认值
}
if asc {
return db.Order(sortField + " ASC")
}
return db.Order(sortField + " DESC")
}
// 使用
db = SafeOrder(db, userInputSortField, true)
注意:
ORDER BY后的字段无法使用参数绑定,必须通过白名单校验防止注入。
模糊查询安全
参数化 LIKE 查询
Go
// 正确:参数中拼接通配符
keyword := "%张%"
db.Where("name LIKE ?", keyword).Find(&users)
// 错误:SQL 中拼接
db.Where("name LIKE '%" + keyword + "%'").Find(&users) // 危险
// 安全封装
func LikeQuery(db *gorm.DB, field, value string) *gorm.DB {
return db.Where(field+" LIKE ?", "%"+value+"%")
}
批量插入安全
参数化批量插入
Go
// GORM 自动参数化批量插入
users := []User{
{Name: "张三", Age: 20},
{Name: "李四", Age: 25},
}
db.Create(&users)
// 生成 SQL: INSERT INTO users (name, age) VALUES (?, ?), (?, ?)
// 原生批量插入也需参数化
db.Exec(
"INSERT INTO users (name, age) VALUES (?, ?), (?, ?)",
"张三", 20, "李四", 25,
)
防注入检查清单
安全实践汇总
| 场景 | 安全做法 | 危险做法 |
|---|---|---|
| Where 条件 | Where("id = ?", id) | Where("id = " + id) |
| Raw 查询 | Raw("SELECT ... WHERE id = ?", id) | Raw("SELECT ... WHERE id = " + id) |
| 排序字段 | 白名单校验 | 直接使用用户输入 |
| LIKE 查询 | Like "%"+value+"%" 作为参数 | SQL 中拼接通配符 |
| 表名/列名 | 白名单或硬编码 | 用户输入直接使用 |
注意: 表名和列名无法使用参数绑定,必须通过白名单校验或硬编码,严禁直接使用用户输入。
要点总结
- GORM 的
Where()方法默认使用参数化查询,安全防注入 - 严禁使用字符串拼接用户输入构建 SQL 条件
Raw()查询必须使用?占符绑定参数,禁止拼接ORDER BY后的排序字段需白名单校验,无法参数化- 表名和列名无法参数化,必须硬编码或白名单校验
- LIKE 查询应将通配符拼接到参数值中,而非 SQL 中
存放路径:D:\git2\jwdev\articles\GORM\专家\安全与数据保护\SQL 注入防护.md
📝 发现内容有误?点击此处直接编辑