

新闻资讯
技术学院Go中SQL注入防护的核心是始终使用占位符参数化查询,禁用字符串拼接;表名列名等动态部分须白名单校验;ORM的Raw方法需显式用占位符,输入过滤不能替代参数化。
database/sql 的 Query 和 Exec 配合占位符Go 标准库的 database/sql 本身不拼接 SQL 字符串,只要不用 fmt.Sprintf 或字符串拼接构造查询语句,就能避开绝大多数注入风险。关键在于始终使用问号占位符(?)或命名参数(如 $1、:name,取决于驱动),由底层驱动做参数绑定
。
例如 PostgreSQL 驱动(lib/pq)用 $1,MySQL 驱动(go-sql-driver/mysql)用 ?:
rows, err := db.Query("SELECT name FROM users WHERE id = $1 AND status = $2", userID, "active")如果硬写成:
db.Query("SELECT name FROM users WHERE id = " + strconv.Itoa(userID))——这就直接把变量塞进 SQL 字符串里,完全失去参数化保护。
sql.Named 是命名参数的封装,本质仍是绑定,不是字符串替换fmt.Sprintf 拼接 SQL 查询条件常见错误是动态构建 WHERE 子句时,把用户输入直接插进字符串:
query := fmt.Sprintf("SELECT * FROM products WHERE category = '%s'", r.URL.Query().Get("cat"))攻击者传入 cat=electronics' OR '1'='1 就能绕过条件。这种写法在 Go 里没有语法错误,但等于放弃全部防护能力。
if 判断是否添加 AND price > ?
validCols := map[string]bool{"name": true, "price": true},再查表ORDER BY 这类语句,只接受预设值:if order == "price" { query += " ORDER BY price" }
Raw 方法GORM、SQLx 等库提供 Raw 或 Session.Raw 方法执行原生 SQL。这些方法**不会自动参数化**传入的第二个及后续参数,除非显式使用占位符:
db.Raw("SELECT * FROM users WHERE email = ?", email).Scan(&user)但下面这个就危险:
db.Raw(fmt.Sprintf("SELECT * FROM users WHERE email = '%s'", email)).Scan(&user)——和手写 fmt.Sprintf 一样失效。
Raw 的第一个参数是 SQL 模板,后面才是参数;它不解析 SQL 字符串里的引号或变量Where("email = ?", email) 是安全的,但 Where("email = '" + email + "'") 不是Scopes 或链式 Where 替代拼接,比依赖 Raw 更可控有人给字符串加 strings.ReplaceAll(input, "'", "''") 或用正则删掉分号,这是典型误区。SQL 注入不只靠单引号,还可用十六进制、Unicode 编码、注释符(/* */)、函数嵌套等方式绕过。PostgreSQL 支持 CHR(39) 构造单引号,MySQL 支持 0x27,这些都逃得过简单替换。
mysql_real_escape_string,也不该有——因为参数化才是唯一正解strconv.Atoi)、正则白名单(如邮箱格式)是合理的,但它们是辅助校验,不是注入防护主力参数绑定这件事,在 Go 里只要不主动破坏,基本是默认安全的。真正容易出问题的地方,永远是开发者以为“我只拼了一小段 SQL,应该没事”,结果那一小段就成了整个防线的突破口。