不该写通用字符串过滤函数,因sql注入本质是语义混淆,关键词过滤易被绕过且误杀合法内容;go的database/sql通过占位符(?或$1)+类型安全参数天然防注入,前提是禁用fmt.sprintf和字符串拼接。

Go 项目里写“防 SQL 注入的过滤模块”本身是个危险信号——sql 包原生不拼接 SQL,真正该防的不是“输入”,而是“绕过参数化查询的写法”。别写过滤器,先确保你没在用 fmt.Sprintf 或字符串拼接构造查询。
为什么不该写通用字符串过滤函数
SQL 注入的本质是语义混淆:把用户输入当代码执行。任何基于关键词(如 '、OR、UNION)的字符串过滤,都会漏掉编码绕过、注释符截断、多字节编码歧义等场景,还可能误杀合法内容(比如用户名含单引号)。Go 的 database/sql 用占位符(? 或 $1)+ 类型安全参数,天然隔绝注入——前提是别绕开它。
- 别写
SanitizeSQLInput()这类函数,它给开发者虚假安全感 - 别对
string做全局“清洗”,字段含义不同(邮箱、JSON、富文本),清洗逻辑必然冲突 - ORM 如
gorm默认也走参数化,但注意Where("name = ?", name)安全,Where("name = '" + name + "'")直接崩盘
真正该检查的三处硬编码漏洞
实际审计中,90% 的注入来自这三类写法,比写过滤器优先级高得多:
-
db.Query(fmt.Sprintf("SELECT * FROM users WHERE id = %s", id))—— 用db.Query("SELECT * FROM users WHERE id = ?", id)替代 -
rawQuery := "INSERT INTO logs (msg) VALUES ('" + msg + "')" // 即使 msg 已“过滤”也不行—— 改用db.Exec("INSERT INTO logs (msg) VALUES (?)", msg) -
sort := r.URL.Query().Get("sort"); db.Query("SELECT * FROM posts ORDER BY " + sort)—— 字段名不能参数化,必须白名单校验:if sort != "created_at" && sort != "title" { return ErrInvalidSort }
需要动态表名/列名时的白名单方案
参数化查询无法处理表名、列名、排序方向(ASC/DESC),这时必须用白名单,而不是正则或“转义”:
func buildQuery(table string, order string) (string, error) {
validTables := map[string]bool{"users": true, "posts": true}
validOrders := map[string]bool{"id": true, "created_at": true}
if !validTables[table] {
return "", fmt.Errorf("invalid table: %s", table)
}
if !validOrders[order] {
return "", fmt.Errorf("invalid order field: %s", order)
}
return fmt.Sprintf("SELECT * FROM %s ORDER BY %s DESC", table, order), nil
}
注意:fmt.Sprintf 在这里安全,因为变量已确定来自白名单,不是用户原始输入。
JSON 字段或模糊搜索的边界处理
当必须把用户输入嵌入 SQL 片段(如 JSON_CONTAINS 或 LIKE 模糊匹配),唯一可靠方式是数据库层转义:
- MySQL:
db.Query("SELECT * FROM users WHERE JSON_CONTAINS(profile, ?)", jsonStr)——jsonStr仍走参数化,MySQL 自动处理 - PostgreSQL:
db.Query("SELECT * FROM users WHERE name ILIKE $1", "%"+search+"%")——%符号加在 Go 变量里,不是拼进 SQL 字符串 - 绝对避免:
"WHERE name LIKE '%" + search + "%'",哪怕你调了strings.ReplaceAll(search, "%", "\%"),也挡不住双重编码或 Unicode 归一化绕过
最常被忽略的点:ORM 的 Raw() 方法和数据库驱动的 ExecContext 仍要求手动参数化;还有,日志里打印 SQL 时如果拼接了用户输入,会把敏感数据打出来——这不算注入,但算信息泄露。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











