如何在Golang中防止SQL注入_Web安全编码实践

7次阅读

Go 中 SQL 注入防护的核心是始终使用占位符参数化查询,禁用字符串拼接;表名列名等动态部分须白名单校验;ORM 的 Raw 方法需显式用占位符,输入过滤不能替代参数化。

如何在 Golang 中防止 SQL 注入_Web 安全编码实践

database/sqlQueryExec 配合占位符

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 字符串里,完全失去参数化保护。

  • 所有用户输入(URL 参数、表单字段、Header 值)必须走占位符传参,不能拼进 SQL 字符串
  • 表名、列名、ORDER BY 字段等无法参数化的部分,只能白名单校验或映射转换,不能靠“转义”糊弄
  • 注意:sql.Named 是命名参数的封装,本质仍是绑定,不是字符串替换

避免用 fmt.Sprintf 拼接 SQL 查询条件

常见错误是动态构建 WHERE 子句时,把用户输入直接插进字符串:

立即学习go 语言免费学习笔记(深入)”;

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"}

警惕 ORM 中的原始 SQL 和 Raw 方法

GORM、SQLx 等库提供 RawSession.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 字符串里的引号或变量
  • GORM 的 Where("email = ?", email) 是安全的,但 Where("email ='" + email + "'") 不是
  • Scopes 或链式 Where 替代拼接,比依赖 Raw 更可控

别信“输入过滤”或“单引号转义”能防注入

有人给字符串加 strings.ReplaceAll(input, "'","''") 或用正则删掉分号,这是典型误区。SQL 注入不只靠单引号,还可用十六进制、Unicode 编码、注释符(/* */)、函数嵌套等方式绕过。PostgreSQL 支持 CHR(39) 构造单引号,MySQL 支持 0x27,这些都逃得过简单替换。

  • Go 没有类似 PHP 的 mysql_real_escape_string,也不该有——因为参数化才是唯一正解
  • 对输入做长度限制、类型断言(strconv.Atoi)、正则白名单(如 邮箱 格式)是合理的,但它们是辅助校验,不是注入防护主力
  • 真正难处理的是动态排序、动态表名、动态列——这些问题的答案从来不是“怎么转义”,而是“能不能换种设计绕过去”

参数绑定这件事,在 Go 里只要不主动破坏,基本是默认安全的。真正容易出问题的地方,永远是开发者以为“我只拼了一小段 SQL,应该没事”,结果那一小段就成了整个防线的突破口。

text=ZqhQzanResources