跳到主要内容
版本:2.9.x(Latest)

会话固定攻击简介

会话固定攻击(Session Fixation Attacks)是一种常见的Web应用安全漏洞,攻击者通过设置用户的会话标识符(Session ID)来控制用户的会话。这种攻击的基本流程如下:

  1. 攻击者获取一个有效的 Session ID(通过访问目标网站或其他方式)
  2. 攻击者诱使受害者使用这个Session ID访问目标网站(例如通过特制的URL、XSS攻击等)
  3. 受害者使用这个预设的Session ID登录网站
  4. 登录成功后,该Session ID与受害者的账户关联
  5. 攻击者使用同一个Session ID访问网站,从而获得受害者的身份和权限

这种攻击的危害在于,攻击者无需破解用户的密码就能获取用户的身份,从而访问用户的敏感信息或执行未授权的操作。

防范会话固定攻击

防范会话固定攻击的最佳实践是在用户成功认证(登录)后重新生成Session ID。这样,即使攻击者设法让用户使用特定的Session ID,在登录成功后该 ID 也会被替换,从而使攻击失效。

GoFrame 框架提供了 RegenerateIdMustRegenerateId 方法来实现这一安全机制。

RegenerateId 方法

在一些安全性要求较高的应用场景中,为了防止会话固定攻击Session Fixation Attacks),通常需要在用户登录成功后重新生成 Session IDGoFrame 框架提供了 RegenerateIdMustRegenerateId 方法来实现这一功能。

实现原理

RegenerateId 方法的实现原理如下:

  1. 生成新的Session ID
  2. 将当前会话数据复制到新的Session ID
  3. 根据deleteOld参数决定是否删除旧的会话数据
  4. 更新当前会话的ID为新生成的 ID

这样就实现了会话数据的无缝迁移,同时保证了安全性。

RegenerateId 方法

  • 说明:RegenerateId 方法用于为当前会话重新生成一个新的 Session ID,同时保留会话中的所有数据。这在用户登录成功后特别有用,可以防止会话固定攻击。

  • 格式:

    RegenerateId(deleteOld bool) (newId string, err error)
  • 参数说明:

    • deleteOld:指定是否立即删除旧的会话数据
      • 如果为 true:旧的会话数据将被立即删除
      • 如果为 false:旧的会话数据将被保留,并根据其 TTL 自动过期
  • 返回值:

    • newId:新生成的Session ID
    • err:操作过程中可能出现的错误

MustRegenerateId 方法

  • 说明:MustRegenerateId 方法与 RegenerateId 功能相同,但如果操作过程中出现错误,它会直接panic。这在确保Session ID必须被重新生成的场景中非常有用。

  • 格式:

    MustRegenerateId(deleteOld bool) string
  • 参数说明:

    • deleteOld:与 RegenerateId 方法中的参数含义相同
  • 返回值:

    • 新生成的Session ID

使用示例

package main

import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gtime"
)

func main() {
s := g.Server()

// 模拟登录接口
s.BindHandler("/login", func(r *ghttp.Request) {
username := r.Get("username").String()
// password := r.Get("password").String()

// 假设这里进行了用户名密码验证
// password ...

// 验证通过后,存储用户信息到 Session
r.Session.MustSet("user", g.Map{
"username": username,
"login_time": gtime.Now(),
})

// 重要:登录成功后重新生成 Session ID,防止会话固定攻击
// 参数为 true 表示立即删除旧的会话数据
// 返回的session id可以不使用,server将自动通过header返回最新的session id
_, err := r.Session.RegenerateId(true)
if err != nil {
r.Response.WriteJson(g.Map{
"code": 500,
"message": "Session ID 重新生成失败",
})
return
}

r.Response.WriteJson(g.Map{
"code": 0,
"message": "登录成功",
})
})

// 获取用户信息接口
// 注意需要通过header提交session id
s.BindHandler("/user/info", func(r *ghttp.Request) {
user := r.Session.MustGet("user")
if user == nil {
r.Response.WriteJson(g.Map{
"code": 403,
"message": "未登录或会话已过期",
})
return
}
r.Response.WriteJson(g.Map{
"code": 0,
"message": "获取成功",
"data": user,
})
})

// 注销登录接口
s.BindHandler("/logout", func(r *ghttp.Request) {
// 清除所有会话数据
_ = r.Session.RemoveAll()
r.Response.WriteJson(g.Map{
"code": 0,
"message": "注销成功",
})
})

s.SetPort(8000)
s.Run()
}

安全建议

  1. 登录后重新生成 Session ID:在用户成功登录后,始终调用 RegenerateId 方法重新生成Session ID,这是防止会话固定攻击的基本做法。

  2. 敏感操作后重新生成 Session ID:在用户执行密码修改、权限变更等敏感操作后,也应考虑重新生成Session ID

  3. 删除旧会话数据:在重新生成Session ID时,通常建议将 deleteOld 参数设置为 true,立即删除旧的会话数据,防止会话数据被恶意利用。

  4. 使用 HTTPS:始终使用HTTPS协议传输Session ID,防止会话信息被网络安全工具或中间人攻击捕获。

  5. 设置正确的 Cookie 属性:对于存储Session IDCookie,应设置 HttpOnlySecureSameSite 属性,防止XSS攻击和CSRF攻击。