gin默认不防会话固定攻击,因其本身无session功能,依赖第三方库(如gin-contrib/sessions),而这些库默认不禁用未初始化会话(saveuninitialized:true)且不自动更换登录后id,需手动配置store.options关闭saveuninitialized、登录后立即调用session.regenerate()并设置httponly/secure/samesite等cookie安全属性。

登录成功后不调用 session.Regenerate(),或没关 saveUninitialized: true,Session 固定攻击就防不住。
为什么 Gin 的 session 默认不防固定攻击?
Gin 本身不带 Session 功能,依赖第三方库(如 gin-contrib/sessions)实现。这些库默认行为和 PHP/Java 不同:它不会自动拒绝未初始化的会话 ID,也不会在登录后强制换 ID —— 全靠你手动配、手动调。
常见表现是:攻击者发一个带 session_id=abc123 的链接给你,你点开、登录,结果新会话仍沿用这个旧 ID,攻击者立刻接管。
-
saveUninitialized: true(默认值)会让每个未登录用户都分配一个新会话 ID,等于主动给攻击者“预占 ID” - 没调
session.Regenerate(),登录前后 ID 完全不变 - Cookie 缺少
HttpOnly或Secure,ID 可被 XSS 窃取或 HTTP 明文传输
必须关闭 saveUninitialized: true
这是最常被忽略的底层开关。只要它开着,任何未认证请求都会生成会话并分配 ID,攻击者就能提前固定。
正确配置示例(以 cookie store 为例):
store := cookie.NewStore([]byte("your-secret-key"))
store.Options(sessions.Options{
HttpOnly: true,
Secure: true, // 生产环境必须开启
Path: "/",
})
// 关键:禁用未初始化会话
r.Use(sessions.Sessions("mysession", store))
注意:saveUninitialized: false 是 gin-contrib/sessions 的默认行为,但如果你用的是老版本或自定义 store,务必确认它没被覆盖。
批量分析录音转写,输出多维度拓客报告。触发词:录音分析、总结、音频总结、拜访记录总结。当用户提及「分析录音」「看看录音数据」「最近的录音」「通话记录」且意图为批量统计/分析时触发。仅出现「录音」或「拜访」时需结合上下文,若仅查看单条详情则不触发。
验证方式:未登录时访问 /login,检查响应头里有没有 Set-Cookie;没有,说明关成功了。
登录成功后立即调用 session.Regenerate()
这个调用不能晚于重定向前,且必须等回调完成 —— 它是异步的。
- 错误写法:
session.Delete()+session.Set(...),会丢数据、ID 不变 - 正确写法:在认证通过后,立刻调用
session.Regenerate(),并在回调里写入用户信息 - 别在
session.Start()前调用,否则静默失败
示例:
r.POST("/login", func(c *gin.Context) {
// 验证用户名密码...
if valid {
session := sessions.Default(c)
// 必须在这里 Regenerate,不是之前也不是之后
session.Regenerate(func(err error) {
if err != nil {
c.JSON(500, gin.H{"error": "session regenerate failed"})
return
}
session.Set("user_id", userID)
session.Save()
c.Redirect(302, "/dashboard")
})
return
}
c.JSON(401, gin.H{"error": "login failed"})
})
配套 Cookie 安全设置不能漏
换 ID 只是第一步,如果 Cookie 能被 JS 读、能走 HTTP、能透传到 URL,照样白换。
-
HttpOnly: true:阻止 document.cookie 读取,防 XSS 窃取 -
Secure: true:强制 HTTPS 传输,防中间人嗅探(开发环境可设为 false,但上线必须 true) -
SameSite: http.SameSiteStrictMode或http.SameSiteLaxMode:缓解 CSRF -
MaxAge和Path显式设置,避免继承默认宽松值
注意:这些必须在 sessions.Sessions() 中间件注册前设置好,运行时用 store.Options() 配置,而不是靠 c.SetCookie() 补救。
复杂点在于:Gin 的 session 库对 SameSite 支持依赖底层 Go 版本(≥1.11),旧版本只能设字符串,新版才支持枚举值 —— 这个细节容易被忽略,导致 SameSite 失效。










