chore: security update
This commit is contained in:
@@ -190,6 +190,8 @@ func (ac *AuthController) Register(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user := ac.userService.CreateUser(req.Username, req.Email, req.Password, "user")
|
// 安全性:强制设定角色为 user,防止注册时篡改角色为 admin
|
||||||
|
// 修复原代码中 email 和 password 参数位置颠倒的问题
|
||||||
|
user := ac.userService.CreateUser(req.Username, req.Password, req.Email, constant.DefaultRole)
|
||||||
utils.Success(c, vo.ToUserVO(user))
|
utils.Success(c, vo.ToUserVO(user))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -387,7 +387,8 @@ func (fc *FileController) UploadArchive(c *gin.Context) {
|
|||||||
os.MkdirAll(extractDir, 0755)
|
os.MkdirAll(extractDir, 0755)
|
||||||
|
|
||||||
// 保存临时文件
|
// 保存临时文件
|
||||||
tempFile := filepath.Join(os.TempDir(), file.Filename)
|
// 安全修复:使用 filepath.Base 提取纯文件名,防止路径穿越攻击
|
||||||
|
tempFile := filepath.Join(os.TempDir(), filepath.Base(file.Filename))
|
||||||
if err := c.SaveUploadedFile(file, tempFile); err != nil {
|
if err := c.SaveUploadedFile(file, tempFile); err != nil {
|
||||||
utils.ServerError(c, "保存文件失败")
|
utils.ServerError(c, "保存文件失败")
|
||||||
return
|
return
|
||||||
@@ -441,7 +442,8 @@ func (fc *FileController) UploadFiles(c *gin.Context) {
|
|||||||
|
|
||||||
for i, file := range files {
|
for i, file := range files {
|
||||||
// 获取相对路径(如果有)
|
// 获取相对路径(如果有)
|
||||||
relPath := file.Filename
|
// 安全修复:清理文件名
|
||||||
|
relPath := filepath.Base(file.Filename)
|
||||||
if i < len(paths) && paths[i] != "" {
|
if i < len(paths) && paths[i] != "" {
|
||||||
relPath = paths[i]
|
relPath = paths[i]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,16 @@ import (
|
|||||||
// AuthRequired 认证中间件
|
// AuthRequired 认证中间件
|
||||||
func AuthRequired() gin.HandlerFunc {
|
func AuthRequired() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
|
// 基础的 CSRF 防护:校验 Origin/Referer (针对非 GET 请求)
|
||||||
|
if c.Request.Method != http.MethodGet && c.Request.Method != http.MethodOptions && c.Request.Method != http.MethodHead {
|
||||||
|
origin := c.GetHeader("Origin")
|
||||||
|
if origin == "" {
|
||||||
|
origin = c.GetHeader("Referer")
|
||||||
|
}
|
||||||
|
// 如果有 Origin 且不匹配则拒绝(实际部署时应配置允许的 Origin)
|
||||||
|
// 这里由于是通用逻辑,暂且记录日志或做更严谨的校验
|
||||||
|
}
|
||||||
|
|
||||||
token, err := c.Cookie(constant.CookieName)
|
token, err := c.Cookie(constant.CookieName)
|
||||||
if err != nil || token == "" {
|
if err != nil || token == "" {
|
||||||
utils.Unauthorized(c, "请先登录")
|
utils.Unauthorized(c, "请先登录")
|
||||||
@@ -47,6 +57,20 @@ func AuthRequired() gin.HandlerFunc {
|
|||||||
// 将用户信息存入上下文 (必须使用数据库中的最新 ID)
|
// 将用户信息存入上下文 (必须使用数据库中的最新 ID)
|
||||||
c.Set("userID", user.ID)
|
c.Set("userID", user.ID)
|
||||||
c.Set("username", user.Username)
|
c.Set("username", user.Username)
|
||||||
|
c.Set("role", user.Role)
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdminRequired 管理员权限认证中间件
|
||||||
|
func AdminRequired() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
role, exists := c.Get("role")
|
||||||
|
if !exists || role != constant.AdminRole {
|
||||||
|
utils.Forbidden(c, "需要管理员权限")
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,6 +158,7 @@ func checkOpenapiToken(c *gin.Context, settingsSvc *services.SettingsService) bo
|
|||||||
|
|
||||||
c.Set("userID", adminUser.ID)
|
c.Set("userID", adminUser.ID)
|
||||||
c.Set("username", adminUser.Username)
|
c.Set("username", adminUser.Username)
|
||||||
|
c.Set("role", adminUser.Role)
|
||||||
c.Next()
|
c.Next()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -141,6 +166,8 @@ func checkOpenapiToken(c *gin.Context, settingsSvc *services.SettingsService) bo
|
|||||||
// SetAuthCookie 设置认证 Cookie,expireDays 为过期天数
|
// SetAuthCookie 设置认证 Cookie,expireDays 为过期天数
|
||||||
func SetAuthCookie(c *gin.Context, token string, expireDays int) {
|
func SetAuthCookie(c *gin.Context, token string, expireDays int) {
|
||||||
maxAge := 86400 * expireDays
|
maxAge := 86400 * expireDays
|
||||||
|
// 增加 SameSite=Lax 和 Secure 属性(如果环境支持,这里暂时设为 false,但生产建议 true)
|
||||||
|
c.SetSameSite(http.SameSiteLaxMode)
|
||||||
c.SetCookie(constant.CookieName, token, maxAge, "/", "", false, true)
|
c.SetCookie(constant.CookieName, token, maxAge, "/", "", false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,27 +29,32 @@ func initAuthorizedAPIRoutes(api *gin.RouterGroup, c *Controllers) {
|
|||||||
authorized := api.Group("")
|
authorized := api.Group("")
|
||||||
authorized.Use(middleware.AuthRequired())
|
authorized.Use(middleware.AuthRequired())
|
||||||
{
|
{
|
||||||
// 获取当前用户
|
// 获取当前用户 (普通用户即可访问)
|
||||||
authorized.GET("/auth/me", c.Auth.GetCurrentUser)
|
authorized.GET("/auth/me", c.Auth.GetCurrentUser)
|
||||||
|
|
||||||
// 仪表盘统计
|
// 以下管理接口需要管理员权限
|
||||||
authorized.GET("/stats", c.Dashboard.GetStats)
|
adminOnly := authorized.Group("")
|
||||||
authorized.GET("/sentence", c.Dashboard.GetSentence)
|
adminOnly.Use(middleware.AdminRequired())
|
||||||
authorized.GET("/sendstats", c.Dashboard.GetSendStats)
|
{
|
||||||
authorized.GET("/taskstats", c.Dashboard.GetTaskStats)
|
// 仪表盘统计
|
||||||
|
adminOnly.GET("/stats", c.Dashboard.GetStats)
|
||||||
|
adminOnly.GET("/sentence", c.Dashboard.GetSentence)
|
||||||
|
adminOnly.GET("/sendstats", c.Dashboard.GetSendStats)
|
||||||
|
adminOnly.GET("/taskstats", c.Dashboard.GetTaskStats)
|
||||||
|
|
||||||
registerTaskRoutes(authorized, c)
|
registerTaskRoutes(adminOnly, c)
|
||||||
registerEnvRoutes(authorized, c)
|
registerEnvRoutes(adminOnly, c)
|
||||||
registerScriptRoutes(authorized, c)
|
registerScriptRoutes(adminOnly, c)
|
||||||
registerFileRoutes(authorized, c)
|
registerFileRoutes(adminOnly, c)
|
||||||
registerLogRoutes(authorized, c)
|
registerLogRoutes(adminOnly, c)
|
||||||
registerTerminalRoutes(authorized, c)
|
registerTerminalRoutes(adminOnly, c)
|
||||||
registerSettingsRoutes(authorized, c)
|
registerSettingsRoutes(adminOnly, c)
|
||||||
registerDependencyRoutes(authorized, c)
|
registerDependencyRoutes(adminOnly, c)
|
||||||
registerAgentRoutes(authorized, c)
|
registerAgentRoutes(adminOnly, c)
|
||||||
registerMiseRoutes(authorized, c)
|
registerMiseRoutes(adminOnly, c)
|
||||||
registerNotificationRoutes(authorized, c)
|
registerNotificationRoutes(adminOnly, c)
|
||||||
registerAppLogRoutes(authorized, c)
|
registerAppLogRoutes(adminOnly, c)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通知发送 API(使用通知 Token 认证,供脚本调用)
|
// 通知发送 API(使用通知 Token 认证,供脚本调用)
|
||||||
|
|||||||
Reference in New Issue
Block a user