Gin框架本身不防SQL注入,需手动确保数据库操作安全:GORM的Where/First安全,Raw需用参数化查询;原生database/sql须用Query或Prepare带参数;必须手动添加X-Content-Type-Options、X-Frame-Options、Content-Security-Policy等安全响应头。

Gin 框架本身不防 SQL 注入——它连数据库连接都不管,更不会替你检查 db.Raw() 里有没有拼接用户输入。防注入不是“开了 Gin 就自动安全”,而是你每写一行数据库操作,都得主动选对方式。
用 GORM 时,Where 和 First 是安全的,但 Raw 不是
GORM 默认走参数化查询,db.Where("email = ?", email).First(&user) 底层生成的是带占位符的预编译语句,用户输入永远当数据传,不会进 SQL 解析器。但一旦切到 Raw,就等于交出控制权:
- ✅ 安全写法:
db.Raw("SELECT * FROM articles WHERE status = ? AND boss_id = ?", status, bossID).Scan(&list) - ❌ 危险写法:
db.Raw("SELECT * FROM articles WHERE status = " + statusStr).Scan(&list)(字符串拼接) - ⚠️ 注意:
Raw中的?占位符只支持位置参数,不支持命名参数(如:status),GORM 不解析它
database/sql 原生操作必须用 Prepare 或 Query 带参数
绕过 GORM 直连 sql.DB 时,db.Query("SELECT * FROM users WHERE id = " + id) 是典型高危操作。Go 标准库提供了原生防护能力,但不会自动触发:
- ✅ 推荐:
rows, err := db.Query("SELECT name FROM users WHERE id = ?", id)(内部自动调用Prepare) - ✅ 更显式:
stmt, _ := db.Prepare("UPDATE logs SET msg = ? WHERE id = ?"); stmt.Exec(msg, id) - ❌ 绝对禁止:
db.Query("... WHERE id = " + id)、fmt.Sprintf("... %s", id)、strconv.Itoa()后拼接 - ? 提示:即使
id看起来是数字,也要当字符串处理——攻击者可能传"1 OR 1=1"这种字符串给整型字段
三个关键响应头必须手动加,gin.Default() 一个都不带
gin.Default() 只加了日志和恢复中间件,所有安全响应头都要自己补。漏掉任何一个,都可能让前端或 CDN 绕过防护:
-
X-Content-Type-Options: nosniff:防止浏览器把image.jpg?xss=alert(1)当 HTML 执行;用r.Use(func(c *gin.Context) { c.Header("X-Content-Type-Options", "nosniff"); c.Next() }) -
X-Frame-Options: DENY:防点击劫持,尤其管理后台必须设;别在每个 handler 里重复写,统一中间件最稳 -
Content-Security-Policy: default-src 'self':起步就锁死资源加载域,后续再按需放开script-src或img-src - ⚠️ 注意:
Strict-Transport-Security只能在 HTTPS 下发,本地开发用c.Request.TLS != nil判断再设,否则浏览器会报错并拒绝后续 HTTPS 请求
真正容易被忽略的不是“该不该做”,而是“在哪做”——安全头加在路由注册之后、handler 执行之前;SQL 参数绑定写在数据访问层最外沿;而所有 Raw 调用,都应该被 grep -r "db\.Raw" ./ 定期扫一遍,确认没漏掉拼接点。


















