Go语言代码审计实战
字数 982 2025-08-07 08:21:50
Go语言代码审计实战教学文档
0x01 程序一:PPGo审计分析
1.1 程序概述
PPGo是一个基于Go语言的任务调度系统,使用Beego框架开发,GitHub地址:https://github.com/george518/PPGo_Job
1.2 环境搭建
- 下载源码
- 创建数据库并导入
ppgo_job2.sql - 配置
conf/app.conf文件 - 运行
./run.sh start|stop
1.3 登录功能分析
登录请求示例:
POST /login_in HTTP/1.1
Host: maoge:8080
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
username=admin&password=123456
登录成功响应:
HTTP/1.1 200 OK
Set-Cookie: auth=1|c2ef80548b36081206a40745cffbca88; Expires=Thu, 02 Dec 2021 05:12:52 UTC; Max-Age=604800; Path=/
1.4 鉴权机制分析
Beego框架通过Prepare方法实现AOP鉴权,核心代码如下:
func (self *BaseController) auth() {
self.userId = 0
// 从cookie中获取auth值
auth := self.Ctx.GetCookie("auth")
if len(auth) > 0 {
// 分隔auth值
parts := strings.Split(auth, "|")
userId, _ := strconv.Atoi(parts[0])
if userId > 0 {
// 获取用户信息
user, err := models.UserGetById(userId)
if err == nil && user != nil {
// 验证auth值
if parts[1] == self.authEncode(user) {
self.userId = userId
}
}
}
}
// 检查URL权限
if !self.checkAccess() {
if self.userId == 0 {
// 未授权处理
} else {
// 鉴权成功
}
}
}
1.5 鉴权绕过漏洞
漏洞原理:
- 当修改
authcookie中的userid为>1的值时 - 系统会尝试查询该用户信息
- 查询结果为nil(用户不存在)
- 但
self.userId仍保持>0的状态 - 由于URL不在
allowUrl和noAuth列表中 - 但
self.userId>0,导致绕过鉴权
利用方法:
修改cookie为auth=5|任意值即可绕过鉴权
0x02 程序二:文档管理系统审计分析
2.1 登录功能分析
登录核心代码:
if c.Ctx.Input.IsPost() {
account := c.GetString("account")
password := c.GetString("password")
captcha := c.GetString("code")
isRemember := c.GetString("is_remember")
// 验证码检查
if v, ok := c.Option["ENABLED_CAPTCHA"]; ok && strings.EqualFold(v, "true") {
v, ok := c.GetSession(conf.CaptchaSessionName).(string)
if !ok || !strings.EqualFold(v, captcha) {
c.JsonResult(6001, i18n.Tr(c.Lang, "message.captcha_wrong"))
}
}
member, err := models.NewMember().Login(account, password)
if err == nil {
member.LastLoginTime = time.Now()
_ = member.Update("last_login_time")
c.SetMember(*member)
if strings.EqualFold(isRemember, "yes") {
remember.MemberId = member.MemberId
remember.Account = member.Account
remember.Time = time.Now()
v, err := utils.Encode(remember)
if err == nil {
c.SetSecureCookie(conf.GetAppKey(), "login", v, time.Now().Add(time.Hour*24*30).Unix())
}
}
}
}
2.2 密码验证机制
func PasswordVerify(hashing string, pass string) (bool, error) {
data := trimSaltHash(hashing)
interation, _ := strconv.ParseInt(data["interation_string"], 10, 64)
has, err := hash(pass, data["salt_secret"], data["salt"], int64(interation))
if err != nil {
return false, err
}
if (data["salt_secret"] + delmiter + data["interation_string"] + delmiter + has + delmiter + data["salt"]) == hashing {
return true, nil
} else {
return false, nil
}
}
2.3 会话管理机制
if member, ok := c.GetSession(conf.LoginSessionName).(models.Member); ok && member.MemberId > 0 {
c.Member = &member
c.Data["Member"] = c.Member
} else {
var remember CookieRemember
if cookie, ok := c.GetSecureCookie(conf.GetAppKey(), "login"); ok {
if err := utils.Decode(cookie, &remember); err == nil {
if member, err := models.NewMember().Find(remember.MemberId); err == nil {
c.Member = member
c.Data["Member"] = member
c.SetMember(*member)
}
}
}
}
2.4 SecureCookie实现分析
func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) {
vs := base64.URLEncoding.EncodeToString([]byte(value))
timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
h := hmac.New(sha256.New, []byte(Secret))
fmt.Fprintf(h, "%s%s", vs, timestamp)
sig := fmt.Sprintf("%02x", h.Sum(nil))
cookie := strings.Join([]string{vs, timestamp, sig}, "|")
ctx.Output.Cookie(name, cookie, others...)
}
应用密钥获取:
func GetAppKey() string {
return web.AppConfig.DefaultString("app_key", "mindoc")
}
2.5 鉴权绕过漏洞
漏洞原理:
- 系统使用固定密钥"mindoc"进行cookie加密
- 可以通过构造合法的"login" cookie绕过认证
- 类似于Shiro的RememberMe漏洞
利用方法:
- 构造合法的remember结构体
- 使用"mindoc"作为密钥生成cookie
- 在请求中添加伪造的"login" cookie
0x03 总结与防御建议
3.1 常见漏洞模式
- 鉴权逻辑缺陷:如PPGo中的userid验证不严
- 固定加密密钥:如文档系统中的"mindoc"
- 不安全的会话管理:依赖客户端可控的cookie值
3.2 防御建议
- 严格验证用户身份,确保用户存在且状态正常
- 使用强随机密钥,避免硬编码
- 实现完善的会话失效机制
- 对敏感操作进行二次验证
- 定期进行安全审计和代码审查
3.3 Go语言审计特点
- 框架众多但设计模式相似
- 通常通过中间件或Prepare方法实现鉴权
- 标准库安全性较高,但自定义实现可能存在风险
- 并发特性可能引入特殊的安全问题