17370845950

Golang Web开发中SQL注入如何防范_Golang安全开发实践
Go语言防SQL注入的关键是禁用字符串拼接、强制参数化查询,对表名等标识符须白名单校验,ORM原生查询也需避免拼接。

Go 语言本身不会自动导致 SQL 注入,但用 fmt.Sprintfstrconv 拼接查询字符串,或直接把用户输入塞进 db.Query,就等于把门敞开。

database/sql 的参数化查询代替字符串拼接

Go 标准库的 database/sql 原生支持占位符(?$1 等,取决于驱动),这是防注入最直接有效的手段。关键不是“用了预处理”,而是“没让用户输入参与 SQL 结构生成”。

常见错误写法:

// ❌ 危险:字符串拼接
username := r.URL.Query().Get("user")
rows, _ := db.Query("SELECT * FROM users WHERE name = '" + username + "'")

正确做法:

// ✅ 安全:参数绑定
username := r.URL.Query().Get("user")
rows, _ := db.Query("SELECT * FROM users WHERE name = ?", username)
  • MySQL 驱动(github.com/go-sql-driver/mysql)用 ?
  • PostgreSQL 驱动(github.com/lib/pq)用 $1, $2
  • SQLite 驱动(github.com/mattn/go-sqlite3)也支持 ? 和命名参数(:name
  • 不要试图自己转义引号或过滤单引号——绕过方式太多,且易漏

避免在查询中动态拼接表名、字段名或 ORDER BY 子句

参数化查询只适用于值(value),不适用于标识符(identifier):表名、列名、排序方向(ASC/DESC)无法用 ? 占位。这类场景必须白名单校验。

例如,允许前端指定排序字段:

// ❌ 错误:直接拼接
sortField := r.URL.Query().Get("sort")
rows, _ := db.Query("SELECT * FROM posts ORDER BY " + sortField + " DESC")

应改为:

// ✅ 白名单控制
validSortFields := map[string]bool{"title": true, "created_at": true, "author_id": true}
sortField := r.URL.Query().Get("sort")
if !validSortFields[sortField] {
    http.Error(w, "invalid sort field", http.StatusBadRequest)
    return
}
rows, _ := db.Query("SELECT * FROM posts ORDER BY " + sortField + " DESC")
  • 永远不要信任用户输入来决定 SQL 结构部分
  • map[string]boolswitch 列出所有合法值,拒绝其余一切
  • ORDER BY 后的 ASC/DESC 同理,不能靠字符串替换或正则“过滤”

慎用 sql.Raw 和第三方 ORM 的原生查询接口

GORMRaw()Session().Exec(),或 sqlxQueryx 带格式化字符串,都可能绕过参数化逻辑。只要里面出现 fmt.Sprintf+ 拼接,风险就回来了。

例如 GORM 中的典型误用:

// ❌ 危险:Raw 中拼接
name := r.FormValue("name")
db.Raw("SELECT * FROM users WHERE name = '" + name + "'").Scan(&users)

应统一走参数化路径:

// ✅ 正确:Raw 支持问号占位
name := r.FormValue("name")
db.Raw("SELECT * FROM users WHERE name = ?", name).Scan(&users)
  • GORM v2+ 的 Whe

    re()
    方法默认安全(内部用参数绑定),优先用它
  • 如果必须用 Raw,确保所有用户输入都作为参数传入,而非拼进 SQL 字符串
  • 注意某些 ORM 的“命名参数”语法(如 :name)仍需配合 sql.Named 或对应驱动支持,不能混用

真正难防的不是技术点,而是开发时那一秒的“我就拼一下,反正这个字段前端可控”。SQL 注入漏洞往往藏在日志查询、导出接口、管理后台的模糊搜索里——这些地方最容易被当成“低风险”而跳过参数化。只要用户输入进了 SQL 字符串字面量,就该立刻停下来检查。