sql注入防护应优先使用参数化查询而非黑名单清洗,因后者易被绕过;动态场景需白名单校验与类型强校验。

SQL注入清洗函数不能只靠黑名单字符替换
直接对输入字符串做 strings.ReplaceAll 替换单引号、分号、-- 等字符,看似简单,实则无效。攻击者可用十六进制编码(0x27)、宽字节(%A1%BF)、注释绕过(/* */)或大小写混用(SeLeCt)绕过简单过滤。这类函数如果部署在应用层,反而会制造虚假安全感。
应该优先使用 database/sql 的参数化查询
Go 标准库的 database/sql 原生支持占位符参数绑定,这才是防 SQL 注入的正确起点。所有用户输入必须走 ?(MySQL/SQLite)或 $1(PostgreSQL)占位符,由驱动完成类型安全转义:
rows, err := db.Query("SELECT * FROM users WHERE name = ? AND age > ?", userName, userAge)
注意:userName 和 userAge 是变量,不是拼接进 SQL 字符串的。一旦你发现代码里有 fmt.Sprintf("WHERE name = '%s'", input) 或 +" '"+input+"'",就该立刻重构,而不是加清洗函数。
真需要预处理时,只对非参数化场景做最小化白名单校验
极少数情况无法用参数化(如动态表名、排序字段、LIMIT 数值),此时清洗逻辑必须是白名单 + 类型强校验,而非模糊替换:
-
tableName只允许匹配正则^[a-zA-Z_][a-zA-Z0-9_]{0,63}$,且需在预定义表名集合中查得 -
orderBy字段只能是[]string{"id", "created_at", "status"}中的一个,用slice.Contains判断 -
limit必须用strconv.Atoi转整数,再限制范围(如0 )
不要写“过滤掉所有 ; 和 UNION”,那没意义;要写“这个字段只接受这 3 个值,其它一律拒绝”。
别把 html.EscapeString 当 SQL 清洗用
html.EscapeString 是为输出到 HTML 页面设计的,它把 ' 变成 ',但数据库根本不会解析这个实体——结果只是把一个合法字符串存成了乱码,还可能引发后续逻辑错误。同理,url.QueryEscape、json.Marshal 都不解决 SQL 注入问题。混淆编码上下文是常见误区。
真正难的不是写清洗函数,而是识别哪些地方本就不该接收原始字符串——比如 API 请求体里的 sort_field,应该在路由或中间件层就映射为枚举值,而不是传到 DAO 层再“清洗”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











