防止SQL注入最有效的方式是使用参数化查询,通过预编译语句实现数据与代码分离,确保用户输入仅作为数据绑定而非SQL指令执行;Gin框架需手动对c.Query、c.PostForm等获取的数据进行参数化处理,禁用字符串拼接。

直接用 gin.Context 拼 SQL 或原样输出用户输入,等于把数据库和前端页面同时交给攻击者操作。Gin 本身不自动防 SQL 注入或 XSS,得靠你写代码时卡住关键位置。
SQL 注入:别拼字符串,用参数化查询
所有从 c.Query、c.PostForm、c.Param 拿到的数据,只要进 SQL,就必须走参数绑定。Gin 不管你用什么数据库驱动,但如果你用 database/sql + mysql 或 postgres 驱动,就该用 ? 或 $1 占位符。
- 错误示范:
db.QueryRow("SELECT * FROM users WHERE name = '" + c.Query("name") + "'")—— 单引号一绕,' OR 1=1 --就能登录任意账号 - 正确做法:用
db.QueryRow("SELECT * FROM users WHERE name = ?", c.Query("name"))(MySQL)或db.QueryRow("SELECT * FROM users WHERE name = $1", c.Query("name"))(PostgreSQL) - 如果用 GORM,确保用
Where("name = ?", name)而不是Where("name = '" + name + "'");GORM 的Where方法支持参数化,但括号里写死字符串就失效 - 特别注意
IN子句:不能直接传切片进去,得动态生成占位符,比如WHERE id IN (?, ?, ?),再把切片元素逐个传参
XSS:输出前转义,别信任何 c.Query 或 c.PostForm 的值
Gin 没有模板自动转义机制(不像 Django 或 Laravel),所有返回 HTML 的地方,你得自己调用 html.EscapeString 或用安全的模板函数。
- 渲染 HTML 页面时:用
html/template而不是text/template,并在模板里用{{.UserName}}(自动转义)而不是{{.UserName | safeHTML}} - JSON 接口也要防:如果字段内容会回填到前端 JS 变量里,比如
var name = "{{.Name}}";,必须用js.EscapeString,否则"和\会被执行 - URL 参数输出到 href 或 src 时,用
url.PathEscape或url.QueryEscape,不能直接插字符串 - 别依赖前端过滤:
<script>alert(1)</script>在后端没处理,前端再怎么DOMPurify.sanitize都晚了——存储型 XSS 已经落库
HTTP 响应头补位:CSP 和 HttpOnly 是低成本高收益项
光靠代码层过滤不够,浏览器层面加一层限制能拦住大量反射型和 DOM 型 XSS。
- 加 CSP 头:在 Gin 中间件里写
c.Header("Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline'")—— 先放开unsafe-inline过渡,后续逐步收紧,禁掉内联脚本 - Cookie 必须设
HttpOnly:用c.SetCookie("session_id", token, 3600, "/", "example.com", true, true),最后两个true分别对应HttpOnly和Secure - 别漏掉
X-Content-Type-Options: nosniff和X-Frame-Options: DENY,防止 MIME 类型混淆和点击劫持 - 这些头对静态资源也生效,所以要确认你引入的 CDN 脚本在 CSP 白名单里,否则页面直接报错
最易被忽略的是「数据落地」环节:用户提交的内容存进数据库后,下次读出来渲染前是否还做了一次转义?很多团队只防了入口,忘了出口。XSS 不是“存的时候过滤一次”就完事,而是“每次输出到不同上下文(HTML/JS/URL)都得按规则编码一次”。


















