参数化查询是防止sql注入的强制性措施,必须使用占位符(如?)而非字符串拼接,gin框架不自动防护,dao层需严格遵循;gorm中where("email = ?", email)安全,而where("email = "+email)不安全;原生sql须用db.raw()并传参,禁用fmt.sprintf插入变量。

SQL 注入:参数化查询不是可选项,是强制项
只要用 db.Query 或 db.Exec 拼接用户输入,就等于给攻击者开了数据库后门。Gin 本身不处理 SQL 安全,责任完全落在你写的 DAO 层。
- 永远不用
"SELECT * FROM users WHERE name = '" + name + "'"这类字符串拼接 - 用
database/sql的占位符:db.Query("SELECT * FROM users WHERE id = ?", id) - 如果用 GORM,
db.Where("email = ?", email).First(&user)是安全的;但db.Where("email = " + email).First(&user)直接报废 - 原生 SQL 必须走
db.Raw("UPDATE logs SET msg = ? WHERE id = ?", msg, id),绝不能用fmt.Sprintf插入变量
XSS:输出编码比输入过滤更可靠
前端传来的 <script>alert(1)</script> 不一定非要拦在入口,关键是它最终不会被浏览器当成 JS 执行。
- 返回 HTML 页面时,优先用
html/template,不是text/template——{{.}}自动转义,{{. | safeHTML}}才绕过 - 纯 API 场景(JSON 响应)不用转义,但若把用户输入塞进
c.HTML()或模板里,必须先过template.HTMLEscapeString() - 别依赖正则“过滤 script 标签”,攻击者用
<img onerror="alert(1)">就能绕过 - 对富文本内容,用专门的净化库(如
bluemonday),而不是手写白名单
认证中间件:JWT 验证必须校验签名和过期,不能只查字段
很多项目把 token 解出来就直接取 user_id,却忘了验证签名是否被篡改、是否已过期。
-
jwt.Parse返回的*Token必须检查Valid字段,不能只看err == nil - 密钥必须从环境变量或 secret manager 加载,禁止硬编码在代码里
- 不要用 HS256 且密钥太短(比如
"mykey"),至少 32 字节随机字符串 - 刷新令牌(refresh token)必须单独存储、绑定设备指纹、设短过期时间,不能和 access token 共享逻辑
文件上传:路径遍历和类型伪造是高频失守点
用户上传 ../../../etc/passwd 当文件名,或把木马改成 shell.php.jpg,服务器一保存就中招。
- 永远不用
c.FormFile("file")得到的Filename直接拼路径 —— 改用uuid.New().String() + ".jpg"重命名 - 用
mimeType := file.Header.Get("Content-Type")做校验不可靠,要读前几百字节用net/http.DetectContentType或filetype库识别真实类型 - 保存路径必须限制根目录,比如
filepath.Join(uploadDir, safeName),再用filepath.EvalSymlinks确认没逃出范围 - 上传目录禁止执行权限,Linux 上设
chmod 755 upload/ && chmod -x upload/
实际部署时,最容易被忽略的是错误响应泄露信息 —— 500 Internal Server Error 带堆栈、数据库字段名、Go 版本号,都是攻击者的路标。所有生产环境的 c.Error() 和日志都要脱敏,HTTP 响应体里绝不出现 panic: pq: syntax error 这种东西。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











