使用本地SID二次校验增强管理系统安全性

This commit is contained in:
刘祥超
2024-04-08 10:24:10 +08:00
parent 37441b26f1
commit 33a5c86beb
12 changed files with 195 additions and 24 deletions

View File

@@ -72,6 +72,9 @@ func (this *SessionManager) Read(sid string) map[string]string {
}
func (this *SessionManager) WriteItem(sid string, key string, value string) bool {
// 删除缓存
defer ttlcache.DefaultCache.Delete( "SESSION@" + sid)
// 忽略OTP
if strings.HasSuffix(sid, "_otp") {
return false
@@ -95,6 +98,9 @@ func (this *SessionManager) WriteItem(sid string, key string, value string) bool
}
func (this *SessionManager) Delete(sid string) bool {
// 删除缓存
defer ttlcache.DefaultCache.Delete( "SESSION@" + sid)
// 忽略OTP
if strings.HasSuffix(sid, "_otp") {
return false

View File

@@ -21,6 +21,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
stringutil "github.com/iwind/TeaGo/utils/string"
"net"
@@ -236,7 +237,10 @@ func (this *IndexAction) RunPost(params struct {
}
// 写入SESSION
params.Auth.StoreAdmin(adminId, params.Remember)
var localSid = rands.HexString(32)
this.Data["localSid"] = localSid
this.Data["ip"] = loginutils.RemoteIP(&this.ActionObject)
params.Auth.StoreAdmin(adminId, params.Remember, localSid)
// 记录日志
err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(adminId), oplogs.LevelInfo, this.Request.URL.Path, langs.DefaultMessage(codes.AdminLogin_LogSuccess, params.Username), loginutils.RemoteIP(&this.ActionObject), codes.AdminLogin_LogSuccess, []any{params.Username})

View File

@@ -19,6 +19,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
stringutil "github.com/iwind/TeaGo/utils/string"
"github.com/xlzd/gotp"
"time"
@@ -132,7 +133,10 @@ func (this *OtpAction) RunPost(params struct {
}
// 写入SESSION
params.Auth.StoreAdmin(adminId, params.Remember)
var localSid = rands.HexString(32)
this.Data["localSid"] = localSid
this.Data["ip"] = loginutils.RemoteIP(&this.ActionObject)
params.Auth.StoreAdmin(adminId, params.Remember, localSid)
// 删除OTP SESSION
_, err = this.RPC().LoginSessionRPC().DeleteLoginSession(this.AdminContext(), &pb.DeleteLoginSessionRequest{Sid: sid})

View File

@@ -0,0 +1,14 @@
package login
import (
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Prefix("/login").
GetPost("/validate", new(ValidateAction)).
EndAll()
})
}

View File

@@ -0,0 +1,74 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package login
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index/loginutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/rands"
"net"
)
type ValidateAction struct {
actionutils.ParentAction
}
func (this *ValidateAction) Init() {
this.Nav("", "", "")
}
func (this *ValidateAction) RunGet(params struct {
From string
}) {
this.Data["from"] = params.From
this.Show()
}
func (this *ValidateAction) RunPost(params struct {
Must *actions.Must
LocalSid string
Ip string
}) {
var isOk bool
defer func() {
this.Data["isOk"] = isOk
if !isOk {
loginutils.UnsetCookie(&this.ActionObject)
this.Session().Delete()
}
this.Success()
}()
if len(params.LocalSid) == 0 || len(params.LocalSid) != 32 {
return
}
if len(params.Ip) == 0 {
return
}
if net.ParseIP(params.Ip) == nil {
return
}
if params.LocalSid == this.Session().GetString("@localSid") {
isOk = true
// renew ip and local sid
var newIP = loginutils.RemoteIP(&this.ActionObject)
var newLocalSid = rands.HexString(32)
this.Session().Write("@ip", newIP)
this.Session().Write("@localSid", newLocalSid)
this.Data["ip"] = newIP
this.Data["localSid"] = newLocalSid
return
}
}

View File

@@ -200,18 +200,22 @@ func (this *userMustAuth) BeforeAction(actionPtr actions.ActionWrapper, paramNam
}
// 检查区域
if securityConfig != nil && securityConfig.CheckClientRegion {
var oldClientIP = session.GetString("@ip")
var currentClientIP = loginutils.RemoteIP(action)
if len(oldClientIP) > 0 && len(currentClientIP) > 0 && oldClientIP != currentClientIP {
var oldRegion = loginutils.LookupIPRegion(oldClientIP)
var newRegion = loginutils.LookupIPRegion(currentClientIP)
if newRegion != oldRegion {
var oldClientIP = session.GetString("@ip")
var currentClientIP = loginutils.RemoteIP(action)
if len(oldClientIP) > 0 && len(currentClientIP) > 0 && oldClientIP != currentClientIP {
var oldRegion = loginutils.LookupIPRegion(oldClientIP)
var newRegion = loginutils.LookupIPRegion(currentClientIP)
if newRegion != oldRegion {
if securityConfig != nil && securityConfig.CheckClientRegion {
loginutils.UnsetCookie(action)
session.Delete()
this.login(action)
return false
} else {
// TODO 考虑IP变化时也需要验证主要是考虑被反向代理的情形
action.RedirectURL("/login/validate?from=" + url.QueryEscape(action.Request.URL.String()))
return false
}
}
}

View File

@@ -60,12 +60,13 @@ func (this *UserShouldAuth) BeforeAction(actionPtr actions.ActionWrapper, paramN
}
// StoreAdmin 存储用户名到SESSION
func (this *UserShouldAuth) StoreAdmin(adminId int64, remember bool) {
func (this *UserShouldAuth) StoreAdmin(adminId int64, remember bool, localSid string) {
loginutils.SetCookie(this.action, remember)
var session = this.action.Session()
session.Write("adminId", numberutils.FormatInt64(adminId))
session.Write("@fingerprint", loginutils.CalculateClientFingerprint(this.action))
session.Write("@ip", loginutils.RemoteIP(this.action))
session.Write("@localSid", localSid)
}
func (this *UserShouldAuth) IsUser() bool {

View File

@@ -29,6 +29,7 @@ import (
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/tasks"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/log"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/login"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/logout"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/messages"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes"

View File

@@ -39,14 +39,21 @@ Tea.context(function () {
};
this.submitSuccess = function (resp) {
if (resp.data.requireOTP) {
window.location = "/index/otp?sid=" + resp.data.sid + "&remember=" + (resp.data.remember ? 1 : 0) + "&from=" + window.encodeURIComponent(this.from)
return
}
if (this.from.length == 0) {
window.location = "/dashboard";
} else {
window.location = this.from;
}
// store information to local
localStorage.setItem("sid", resp.data.localSid)
localStorage.setItem("ip", resp.data.ip)
// redirect back
this.$delay(function () {
if (resp.data.requireOTP) {
window.location = "/index/otp?sid=" + resp.data.sid + "&remember=" + (resp.data.remember ? 1 : 0) + "&from=" + window.encodeURIComponent(this.from)
return
}
if (this.from.length == 0) {
window.location = "/dashboard";
} else {
window.location = this.from;
}
})
};
});

View File

@@ -22,10 +22,17 @@ Tea.context(function () {
};
this.submitSuccess = function (resp) {
if (this.from.length == 0) {
window.location = "/dashboard";
} else {
window.location = this.from;
}
// store information to local
localStorage.setItem("sid", resp.data.localSid)
localStorage.setItem("ip", resp.data.ip)
// redirect back
this.$delay(function () {
if (this.from.length == 0) {
window.location = "/dashboard";
} else {
window.location = this.from;
}
})
};
});

View File

@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
{$TEA.VUE}
{$TEA.SEMANTIC}
<title></title>
</head>
<body>
<div>
</div>
</body>
</html>

View File

@@ -0,0 +1,34 @@
Tea.context(function () {
this.$delay(function () {
let sid = localStorage.getItem("sid")
let ip = localStorage.getItem("ip")
if (sid == null || sid.length == 0 || ip == null || ip.length == 0) {
window.location = "/logout"
return
}
this.$post("$")
.params({localSid: sid, "ip": ip})
.post()
.success(function (resp) {
if (!resp.data.isOk) {
window.location = "/logout"
return
}
// renew local data
localStorage.setItem("sid", resp.data.localSid)
localStorage.setItem("ip", resp.data.ip)
// redirect back (MUST delay)
this.$delay(function () {
if (this.from.length > 0) {
window.location = this.from
} else {
window.location = "/dashboard"
}
})
})
})
})