Gin SQL注入防范
SQL 注入是 Web 应用最常见的安全漏洞之一,Gin 框架下需正确使用数据库操作才能有效防范。
SQL注入原理
攻击者通过构造恶意输入,改变原 SQL 语句逻辑,实现未授权数据访问或破坏。
Go
// 危险示例:字符串拼接 SQL
func GetUser(db *sql.DB, username string) (*User, error) {
query := "SELECT * FROM users WHERE username = '" + username + "'"
// 输入: admin' OR '1'='1
// 实际执行: SELECT * FROM users WHERE username = 'admin' OR '1'='1'
// 结果: 返回所有用户
}
参数化查询(核心防护)
使用占位符,让数据库驱动处理参数转义。
Go
// 安全示例:参数化查询
func GetUser(db *sql.DB, username string) (*User, error) {
query := "SELECT * FROM users WHERE username = ?"
row := db.QueryRow(query, username)
// 数据库驱动自动处理特殊字符转义
}
// Gin 路由中使用
func GetUserHandler(c *gin.Context) {
username := c.Param("username")
user, err := GetUser(db, username)
if err != nil {
c.JSON(500, gin.H{"error": "查询失败"})
return
}
c.JSON(200, user)
}
GORM 安全使用
Go
// 安全:使用 Where 参数绑定
user := User{}
db.Where("username = ?", username).First(&user)
// 安全:使用 First 的主键查询
db.First(&user, userId) // 自动参数化
// 危险:原生 SQL 拼接
db.Raw("SELECT * FROM users WHERE username = '" + username + "'").Scan(&user)
// 安全:原生 SQL 参数化
db.Raw("SELECT * FROM users WHERE username = ?", username).Scan(&user)
// 安全:Map 条件查询
db.Where(map[string]interface{}{"username": username}).First(&user)
// 安全:Struct 条件查询
db.Where(&User{Username: username}).First(&user)
输入验证与过滤
Go
// 使用 validator 验证输入
type LoginRequest struct {
Username string `form:"username" binding:"required,alphanum,min=3,max=20"`
Password string `form:"password" binding:"required,min=6,max=50"`
}
func LoginHandler(c *gin.Context) {
var req LoginRequest
if err := c.ShouldBind(&req); err != nil {
c.JSON(400, gin.H{"error": "参数格式错误"})
return
}
// 验证通过,进行数据库查询
}
常见注入场景与防护
| 场景 | 危险写法 | 安全写法 |
|---|---|---|
| 条件查询 | "WHERE id=" + id | Where("id = ?", id) |
| 模糊查询 | "LIKE '%" + keyword + "%'" | Where("name LIKE ?", "%"+keyword+"%") |
| 排序字段 | "ORDER BY " + sort | 白名单验证排序字段 |
| 表名拼接 | "FROM " + table | 禁止动态表名或严格白名单 |
排序字段白名单验证
Go
var allowedSortFields = map[string]bool{
"id": true,
"created_at": true,
"updated_at": true,
"name": true,
}
func validateSortField(field string) string {
if allowedSortFields[field] {
return field
}
return "id" // 默认排序字段
}
func ListUsers(c *gin.Context) {
sort := c.DefaultQuery("sort", "id")
sort = validateSortField(sort)
order := c.DefaultQuery("order", "asc")
if order != "asc" && order != "desc" {
order = "asc"
}
var users []User
db.Order(sort + " " + order).Find(&users)
c.JSON(200, users)
}
注意:排序字段和表名无法使用参数化,必须使用白名单严格验证。
要点总结
- 始终使用参数化查询,禁止字符串拼接 SQL
- GORM 使用
Where("field = ?", value)而非字符串拼接 - 输入验证是第一道防线,使用 validator 约束格式
- 排序、表名等动态字段使用白名单验证
- 代码审计时搜索
db.Raw(、Exec(、字符串拼接模式 - 定期使用 SQL 注入扫描工具检测
📝 发现内容有误?点击此处直接编辑