iris 本身不制造 sql 注入漏洞,因其输入解析方法(如 ctx.query、ctx.bind)不参与 sql 构建,真正的防护依赖 database/sql 的参数化查询机制;必须严格使用占位符(? 或 $1)配合参数切片,禁用字符串拼接,动态字段名等需白名单校验。

只要不用字符串拼接构造 SQL,Iris 本身不制造 SQL 注入漏洞;真正起作用的是 database/sql 的参数化查询机制,不是框架层的“防护开关”。
为什么 Iris 不需要额外防注入
Iris 的 ctx.Query、ctx.ReadJSON、ctx.Bind 等方法只负责解析输入,不参与 SQL 构建。最终执行查询靠的是你传给 db.Query 或 db.Exec 的语句和参数——而 Go 标准库的 database/sql 强制要求使用占位符(? 或 $1)+ 参数切片,底层驱动(如 mysql)会把参数作为独立数据发送,数据库绝不会将其解析为 SQL 代码。
- 错误写法(绝对禁止):
db.Query("SELECT * FROM users WHERE name = '" + name + "'")—— 字符串拼接,直接中招 - 正确写法:
db.Query("SELECT * FROM users WHERE name = ?", name)—— 参数被隔离传输,安全 - ORM 如 GORM 也默认走 prepare + bind,但要注意关闭
AllowGlobalUpdate等危险选项
哪些地方最容易漏掉参数化
常见于动态条件拼接、IN 子句、ORDER BY / LIMIT 动态字段名等场景,这些无法用 ? 占位,必须手动白名单校验。
-
ORDER BY后的字段名不能参数化:用白名单判断if !slices.Contains([]string{"name", "created_at"}, sortField) { return errors.New("invalid sort field") } -
IN (?, ?, ?)的问号个数必须与参数数量严格匹配:不能硬编码,要用strings.Repeat("?,", len(ids)-1) + "?"拼出占位符串,再db.Query(fmt.Sprintf("... IN (%s)", placeholders), ids...) - 表名、列名、函数名(如
UPPER())一律不可来自用户输入,必须由服务端逻辑决定或查配置表
结构体绑定后直接进 SQL 的风险
很多人用 ctx.Bind(&input) 解析 JSON,再把 input.Name 直接塞进 SQL——这看似安全,实则隐藏陷阱:若结构体字段是 *string 或带默认值,未传字段可能变成空字符串或零值,导致查询逻辑异常(如 WHERE name = '' 匹配到意外数据),虽不属注入,但可能被用于业务逻辑绕过。
- 输入结构体字段应加校验标签:
Name string `json:"name" validate:"required,min=2,max=50,alphanum"` - 对空值/边界值做显式判断:
if input.Name == "" { return ctx.StatusCode(iris.StatusBadRequest).Text("name required") } - 避免在 SQL 中用
LIKE "%"+input.Keyword+"%"—— 应改用db.Query("... LIKE ?", "%"+input.Keyword+"%"),让驱动处理转义
别信“自动过滤”中间件
有人写中间件对所有 ctx.URLParam 或 ctx.FormValue 做正则替换(如删 '、--),这是无效且有害的:
- 过滤时机错:SQL 注入发生在查询执行时,不是请求解析时;删掉单引号反而可能破坏合法输入(如人名 O’Connor)
- 过滤位置错:只扫 URL 参数,漏掉 JSON body、form-data、header 中的恶意输入
- 过滤逻辑错:黑名单永远追不上新攻击变种,且易被编码绕过(
%27、') - 真正该做的是:统一走
db.Query参数化 + 输入结构体校验 + 白名单控制动态 SQL 片段
最危险的不是没写防护,而是误以为框架或中间件已经帮你挡住了——SQL 注入的根因永远在你自己写的那行 db.Query 里,而不是请求入口。每次写 SQL,先盯住占位符,再检查参数类型和来源,比加十个过滤器都管用。











