fix(sql): 修复SQL注入漏洞改用参数化查询

- app.go 将查询条件从字符串拼接改为占位符参数绑定
- config.go 将查询条件从字符串拼接改为占位符参数绑定
- user.go 将查询条件从字符串拼接改为占位符参数绑定
- 移除 app.go 中重复的条件构建逻辑
- 对 group_id、enabled 等参数增加类型校验与错误处理
This commit is contained in:
2026-09-01 21:17:57 +08:00
parent d2c0afa20e
commit a62dbfe74c
3 changed files with 87 additions and 68 deletions
+33 -32
View File
@@ -37,7 +37,9 @@ func (h *AppHandler) List(c *gin.Context) {
} }
userInfo := middleware.GetUserInfo(c) userInfo := middleware.GetUserInfo(c)
query := dao.Engine.Where("enabled = 1")
conditions := []string{"enabled = 1"}
args := make([]interface{}, 0, 6)
if !userInfo.IsSuperAdmin() { if !userInfo.IsSuperAdmin() {
userId, _ := strconv.Atoi(userInfo.Id) userId, _ := strconv.Atoi(userInfo.Id)
@@ -45,42 +47,41 @@ func (h *AppHandler) List(c *gin.Context) {
if userInfo.IsGroupAdmin() && userInfo.GroupId != nil { if userInfo.IsGroupAdmin() && userInfo.GroupId != nil {
// 组管理员:本组应用 + 自己是负责人的应用 // 组管理员:本组应用 + 自己是负责人的应用
query = query.And("(group_id = ? OR FIND_IN_SET('" + userIdStr + "', owner_ids) > 0)", *userInfo.GroupId) conditions = append(conditions, "(group_id = ? OR FIND_IN_SET(?, owner_ids) > 0)")
args = append(args, *userInfo.GroupId, userIdStr)
} else { } else {
// 普通用户:自己是负责人的应用 // 普通用户:自己是负责人的应用
query = query.And("FIND_IN_SET('" + userIdStr + "', owner_ids) > 0") conditions = append(conditions, "FIND_IN_SET(?, owner_ids) > 0")
} args = append(args, userIdStr)
}
var apps []model.Application
whereClause := "enabled = 1"
if !userInfo.IsSuperAdmin() {
userId, _ := strconv.Atoi(userInfo.Id)
userIdStr := strconv.Itoa(userId)
if userInfo.IsGroupAdmin() && userInfo.GroupId != nil {
// 组管理员:本组应用 + 自己是负责人的应用
whereClause += " AND (group_id = " + strconv.Itoa(*userInfo.GroupId) + " OR FIND_IN_SET('" + userIdStr + "', owner_ids) > 0)"
} else {
// 普通用户:自己是负责人的应用
whereClause += " AND (FIND_IN_SET('" + userIdStr + "', owner_ids) > 0)"
} }
} }
if req.AppType != "" { if req.AppType != "" {
whereClause += " AND service_id = '" + req.AppType + "'" conditions = append(conditions, "service_id = ?")
args = append(args, req.AppType)
} }
if req.Name != "" { if req.Name != "" {
whereClause += " AND name LIKE '%" + req.Name + "%'" conditions = append(conditions, "name LIKE ?")
args = append(args, "%"+req.Name+"%")
} }
if req.Code != "" { if req.Code != "" {
whereClause += " AND code LIKE '%" + req.Code + "%'" conditions = append(conditions, "code LIKE ?")
args = append(args, "%"+req.Code+"%")
} }
if req.GroupId != "" { if req.GroupId != "" {
whereClause += " AND group_id = " + req.GroupId groupId, err := strconv.Atoi(req.GroupId)
if err != nil {
c.JSON(http.StatusBadRequest, model.WrapperResponse{Code: 400, Msg: "参数错误"})
return
}
conditions = append(conditions, "group_id = ?")
args = append(args, groupId)
} }
total, _ := dao.Engine.Where(whereClause).Count(&model.Application{}) whereClause := strings.Join(conditions, " AND ")
dao.Engine.Where(whereClause).Limit(req.PageSize, (req.PageNo-1)*req.PageSize).Find(&apps)
var apps []model.Application
total, _ := dao.Engine.Where(whereClause, args...).Count(&model.Application{})
dao.Engine.Where(whereClause, args...).Limit(req.PageSize, (req.PageNo-1)*req.PageSize).Find(&apps)
// load group names - batch query to avoid N+1 // load group names - batch query to avoid N+1
groupIds := make(map[int]bool) groupIds := make(map[int]bool)
@@ -122,13 +123,13 @@ func (h *AppHandler) GetById(c *gin.Context) {
} }
type AddAppRequest struct { type AddAppRequest struct {
Id int `json:"id"` Id int `json:"id"`
GroupId *int `json:"groupId"` GroupId *int `json:"groupId"`
Name string `json:"name" binding:"required"` Name string `json:"name" binding:"required"`
Code string `json:"code" binding:"required"` Code string `json:"code" binding:"required"`
ServiceId string `json:"serviceId"` ServiceId string `json:"serviceId"`
OwnerIds []int `json:"ownerIds"` OwnerIds []int `json:"ownerIds"`
Remarks string `json:"remarks"` Remarks string `json:"remarks"`
} }
func (h *AppHandler) Add(c *gin.Context) { func (h *AppHandler) Add(c *gin.Context) {
+26 -26
View File
@@ -14,8 +14,8 @@ import (
) )
type ConfigHandler struct { type ConfigHandler struct {
parseService *service.ConfigParseService parseService *service.ConfigParseService
permService *service.PermissionService permService *service.PermissionService
} }
func NewConfigHandler(parse *service.ConfigParseService, perm *service.PermissionService) *ConfigHandler { func NewConfigHandler(parse *service.ConfigParseService, perm *service.PermissionService) *ConfigHandler {
@@ -47,38 +47,38 @@ func (h *ConfigHandler) List(c *gin.Context) {
} }
// 构建查询条件 // 构建查询条件
buildConditions := func() []string { conditions := []string{"enabled = 1"}
conditions := []string{"enabled = 1"} args := make([]interface{}, 0, 4)
if req.Env != "" { if req.Env != "" {
conditions = append(conditions, "env = '"+req.Env+"'") conditions = append(conditions, "env = ?")
args = append(args, req.Env)
}
if req.AppId != nil {
if *req.AppId <= 0 {
conditions = append(conditions, "(app_id is null OR app_id = 0)")
} else {
conditions = append(conditions, "app_id = ?")
args = append(args, *req.AppId)
} }
if req.AppId != nil { }
if *req.AppId <= 0 { if !userInfo.IsSuperAdmin() {
conditions = append(conditions, "(app_id is null OR app_id = 0)") if userInfo.IsGroupAdmin() && userInfo.GroupId != nil {
} else { conditions = append(conditions, "group_id = ?")
conditions = append(conditions, "app_id = "+strconv.Itoa(*req.AppId)) args = append(args, *userInfo.GroupId)
} else if len(userInfo.GrantAppIds) > 0 {
ids := make([]string, len(userInfo.GrantAppIds))
for i, id := range userInfo.GrantAppIds {
ids[i] = strconv.Itoa(id)
} }
conditions = append(conditions, "app_id in ("+strings.Join(ids, ",")+")")
} }
if !userInfo.IsSuperAdmin() {
if userInfo.IsGroupAdmin() && userInfo.GroupId != nil {
conditions = append(conditions, "group_id = "+strconv.Itoa(*userInfo.GroupId))
} else if len(userInfo.GrantAppIds) > 0 {
ids := make([]string, len(userInfo.GrantAppIds))
for i, id := range userInfo.GrantAppIds {
ids[i] = strconv.Itoa(id)
}
conditions = append(conditions, "app_id in ("+strings.Join(ids, ",")+")")
}
}
return conditions
} }
conditions := buildConditions()
whereClause := strings.Join(conditions, " AND ") whereClause := strings.Join(conditions, " AND ")
var configs []model.AppConfig var configs []model.AppConfig
total, _ := dao.Engine.Where(whereClause).Count(&model.AppConfig{}) total, _ := dao.Engine.Where(whereClause, args...).Count(&model.AppConfig{})
dao.Engine.Where(whereClause).Limit(req.PageSize, (req.PageNo-1)*req.PageSize).Find(&configs) dao.Engine.Where(whereClause, args...).Limit(req.PageSize, (req.PageNo-1)*req.PageSize).Find(&configs)
// build app names (batch query) and collect configIds for client counts // build app names (batch query) and collect configIds for client counts
configIds := make([]int, 0, len(configs)) configIds := make([]int, 0, len(configs))
+28 -10
View File
@@ -1,12 +1,12 @@
package handler package handler
import ( import (
"crypto/rand"
"kenaito-conf/server/internal/dao" "kenaito-conf/server/internal/dao"
"kenaito-conf/server/internal/middleware" "kenaito-conf/server/internal/middleware"
"kenaito-conf/server/internal/model" "kenaito-conf/server/internal/model"
"kenaito-conf/server/internal/service" "kenaito-conf/server/internal/service"
"kenaito-conf/server/internal/util" "kenaito-conf/server/internal/util"
"crypto/rand"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
@@ -30,30 +30,48 @@ func (h *UserHandler) List(c *gin.Context) {
enabledStr := c.Query("enabled") enabledStr := c.Query("enabled")
userInfo := middleware.GetUserInfo(c) userInfo := middleware.GetUserInfo(c)
buildConditions := func() []string { buildConditions := func() (string, []interface{}) {
conditions := []string{"1=1"} conditions := []string{"1=1"}
args := make([]interface{}, 0, 4)
if !userInfo.IsSuperAdmin() && userInfo.GroupId != nil { if !userInfo.IsSuperAdmin() && userInfo.GroupId != nil {
conditions = append(conditions, "group_id = "+strconv.Itoa(*userInfo.GroupId)) conditions = append(conditions, "group_id = ?")
args = append(args, *userInfo.GroupId)
} }
if groupIdStr != "" { if groupIdStr != "" {
conditions = append(conditions, "group_id = "+groupIdStr) groupId, err := strconv.Atoi(groupIdStr)
if err != nil {
return "", nil
}
conditions = append(conditions, "group_id = ?")
args = append(args, groupId)
} }
if name != "" { if name != "" {
conditions = append(conditions, "name like '%"+name+"%'") conditions = append(conditions, "name LIKE ?")
args = append(args, "%"+name+"%")
} }
if userType != "" { if userType != "" {
conditions = append(conditions, "type = '"+userType+"'") conditions = append(conditions, "type = ?")
args = append(args, userType)
} }
if enabledStr != "" { if enabledStr != "" {
conditions = append(conditions, "enabled = "+enabledStr) enabled, err := strconv.Atoi(enabledStr)
if err != nil {
return "", nil
}
conditions = append(conditions, "enabled = ?")
args = append(args, enabled)
} }
return conditions return strings.Join(conditions, " AND "), args
} }
whereClause := strings.Join(buildConditions(), " AND ") whereClause, args := buildConditions()
if whereClause == "" {
c.JSON(http.StatusBadRequest, model.WrapperResponse{Code: 400, Msg: "参数错误"})
return
}
var users []model.User var users []model.User
dao.Engine.Where(whereClause).Find(&users) dao.Engine.Where(whereClause, args...).Find(&users)
// load group names // load group names
var groups []model.BusinessGroup var groups []model.BusinessGroup