mirror of
https://gitee.com/samwaf/SamWaf.git
synced 2025-12-06 06:58:54 +08:00
@@ -196,6 +196,39 @@ func (w *WafLogAPi) GetHttpCopyMaskApi(c *gin.Context) {
|
||||
response.FailWithMessage("解析失败", c)
|
||||
}
|
||||
}
|
||||
|
||||
// GetAttackIPListApi 获取风险数据列表
|
||||
func (w *WafLogAPi) GetAttackIPListApi(c *gin.Context) {
|
||||
var req request.WafAttackIpTagSearch
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err == nil {
|
||||
ipAttackTags, total, err2 := wafLogService.GetAttackIpListApi(req)
|
||||
if err2 != nil {
|
||||
response.FailWithMessage("访问列表失败:"+err2.Error(), c)
|
||||
} else {
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: ipAttackTags,
|
||||
Total: total,
|
||||
PageIndex: req.PageIndex,
|
||||
PageSize: req.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
} else {
|
||||
response.FailWithMessage("解析失败", c)
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllIpTagApi 获取所有ip tag
|
||||
func (w *WafLogAPi) GetAllIpTagApi(c *gin.Context) {
|
||||
|
||||
ipAttackTags, err2 := wafLogService.GetAllAttackIPTagListApi()
|
||||
if err2 != nil {
|
||||
response.FailWithMessage("访问ip tag 失败:"+err2.Error(), c)
|
||||
} else {
|
||||
response.OkWithDetailed(ipAttackTags, "获取成功", c)
|
||||
}
|
||||
}
|
||||
func GenerateCurlRequest(weblog innerbean.WebLog) string {
|
||||
|
||||
headers := strings.Split(weblog.HEADER, "\n")
|
||||
|
||||
@@ -22,4 +22,5 @@ var (
|
||||
GCONFIG_RECORD_KEEPALIVE_TIME_OUT int64 = 30 // 保持活动超时 默认30s
|
||||
//GCONFIG_RECORD_PATCH_VERSION_CORE int64 = 20250106 // 核心数据库补丁日期
|
||||
//GCONFIG_RECORD_PATCH_VERSION_LOG int64 = 20250106 // 日志数据库补丁日期
|
||||
GCONFIG_RECORD_ALL_SRC_BYTE_INFO int64 = 0 //记录原始信息(默认不开启)
|
||||
)
|
||||
|
||||
@@ -108,6 +108,8 @@ var (
|
||||
GNOTIFY_KAKFA_SERVICE *wafnotify.WafNotifyService //通知服务
|
||||
GNOTIFY_SEND_MAX_LIMIT_MINTUTES = time.Duration(5) * time.Minute // 规则相关信息最大发送抑止 默认5分钟
|
||||
|
||||
/*******日志记录相关*************/
|
||||
GWEBLOG_VERSION = 20250112 //weblog 日志版本号
|
||||
/*********SSL相关*************/
|
||||
GSSL_HTTP_CHANGLE_PATH string = "/.well-known/acme-challenge/" // http01证书验证路径
|
||||
|
||||
|
||||
@@ -33,6 +33,10 @@ type WebLog struct {
|
||||
RISK_LEVEL int `json:"risk_level"` //危险等级 0:正常 1:轻微 2:有害 3:严重 4:特别严重
|
||||
GUEST_IDENTIFICATION string `json:"guest_identification"` //访客身份识别
|
||||
TimeSpent int64 `json:"time_spent"` //用时
|
||||
NetSrcIp string `json:"net_src_ip"` //获取的原始IP
|
||||
SrcByteBody []byte `json:"src_byte_body"` //原始body信息
|
||||
SrcByteResBody []byte `json:"src_byte_res_body"` //返回body bytes信息
|
||||
WebLogVersion int `json:"web_log_version"` //日志版本信息早期的是空和0,后期实时增加
|
||||
}
|
||||
|
||||
// 在 GORM 的 Model 方法中定义复合索引
|
||||
|
||||
@@ -13,3 +13,18 @@ type IPTag struct {
|
||||
Cnt int64 `json:"cnt"` //触发次数
|
||||
Remarks string `json:"remarks"` //备注
|
||||
}
|
||||
type AttackIPTag struct {
|
||||
TenantID string `json:"tenant_id"`
|
||||
UserCode string `json:"user_code"`
|
||||
IP string `json:"ip"`
|
||||
PassNum int64 `json:"pass_num"`
|
||||
DenyNum int64 `json:"deny_num"`
|
||||
FirstTime string `json:"first_time"`
|
||||
LatestTime string `json:"latest_time"`
|
||||
IpTotalTag string `json:"ip_total_tag"`
|
||||
}
|
||||
|
||||
type AllIPTag struct {
|
||||
Label string `json:"label"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
@@ -28,3 +28,9 @@ type WafAttackLogSearch struct {
|
||||
FilterValue string `json:"filter_value" form:"filter_value"` //筛选值
|
||||
request.PageInfo
|
||||
}
|
||||
|
||||
type WafAttackIpTagSearch struct {
|
||||
Rule string `json:"rule" form:"rule"` //规则名
|
||||
SrcIp string `json:"src_ip" form:"src_ip"` //请求IP
|
||||
request.PageInfo
|
||||
}
|
||||
|
||||
@@ -18,4 +18,6 @@ func (receiver *LogRouter) InitLogRouter(group *gin.RouterGroup) {
|
||||
wafLogRouter.GET("/samwaf/waflog/attack/detail", logApi.GetDetailApi)
|
||||
wafLogRouter.GET("/samwaf/waflog/attack/allsharedb", logApi.GetAllShareDbApi)
|
||||
wafLogRouter.GET("/samwaf/waflog/attack/httpcopymask", logApi.GetHttpCopyMaskApi)
|
||||
wafLogRouter.POST("/samwaf/waflog/attack/attackiplist", logApi.GetAttackIPListApi)
|
||||
wafLogRouter.GET("/samwaf/waflog/attack/alliptag", logApi.GetAllIpTagApi)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"SamWaf/common/validfield"
|
||||
"SamWaf/global"
|
||||
"SamWaf/innerbean"
|
||||
"SamWaf/model"
|
||||
"SamWaf/model/request"
|
||||
"SamWaf/wafdb"
|
||||
"errors"
|
||||
@@ -182,6 +183,141 @@ func (receiver *WafLogService) GetUnixTimeByCounter(lastStartCreateUnix int64, l
|
||||
return weblog
|
||||
}
|
||||
|
||||
// GetAttackIpListApi 访问IP列表
|
||||
func (receiver *WafLogService) GetAttackIpListApi(req request.WafAttackIpTagSearch) ([]model.AttackIPTag, int64, error) {
|
||||
var results []model.AttackIPTag
|
||||
var total int64
|
||||
|
||||
// 基础查询部分
|
||||
query := `
|
||||
SELECT
|
||||
tenant_id,
|
||||
user_code,
|
||||
ip,
|
||||
SUM(CASE WHEN ip_tag = '正常' THEN cnt ELSE 0 END) AS pass_num,
|
||||
SUM(CASE WHEN ip_tag <> '正常' THEN cnt ELSE 0 END) AS deny_num,
|
||||
strftime('%Y-%m-%d %H:%M:%S', MIN(update_time)) AS first_time,
|
||||
strftime('%Y-%m-%d %H:%M:%S', MAX(update_time)) AS latest_time,
|
||||
GROUP_CONCAT(DISTINCT CASE WHEN ip_tag <> '正常' THEN ip_tag END) AS ip_total_tag
|
||||
FROM
|
||||
ip_tags
|
||||
WHERE tenant_id=? and user_code=?`
|
||||
|
||||
// 动态添加过滤条件
|
||||
if req.Rule != "" {
|
||||
query += " AND ip_tag = ?"
|
||||
}
|
||||
if req.SrcIp != "" {
|
||||
query += " AND ip = ?"
|
||||
}
|
||||
|
||||
// 完成查询的其他部分
|
||||
query += `
|
||||
GROUP BY
|
||||
tenant_id,
|
||||
user_code,
|
||||
ip
|
||||
HAVING
|
||||
SUM(CASE WHEN ip_tag <> '正常' THEN cnt ELSE 0 END) > 0
|
||||
ORDER BY
|
||||
MAX(update_time) DESC
|
||||
LIMIT ? OFFSET ?`
|
||||
|
||||
// 构建查询参数
|
||||
params := []interface{}{global.GWAF_TENANT_ID, global.GWAF_USER_CODE}
|
||||
|
||||
// 添加 Rule 和 SrcIp 作为参数(如果提供了)
|
||||
if req.Rule != "" {
|
||||
params = append(params, req.Rule)
|
||||
}
|
||||
if req.SrcIp != "" {
|
||||
params = append(params, req.SrcIp)
|
||||
}
|
||||
|
||||
// 分页参数
|
||||
params = append(params, req.PageSize, req.PageSize*(req.PageIndex-1))
|
||||
|
||||
// 执行查询
|
||||
if err := global.GWAF_LOCAL_DB.Raw(query, params...).Scan(&results).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// 获取总记录数
|
||||
countQuery := `
|
||||
SELECT
|
||||
COUNT(*) AS total
|
||||
FROM (
|
||||
SELECT
|
||||
tenant_id,
|
||||
user_code,
|
||||
ip
|
||||
FROM
|
||||
ip_tags
|
||||
WHERE tenant_id=? and user_code=?`
|
||||
|
||||
// 动态添加过滤条件
|
||||
if req.Rule != "" {
|
||||
countQuery += " AND ip_tag = ?"
|
||||
}
|
||||
if req.SrcIp != "" {
|
||||
countQuery += " AND ip = ?"
|
||||
}
|
||||
|
||||
countQuery += `
|
||||
GROUP BY
|
||||
tenant_id,
|
||||
user_code,
|
||||
ip
|
||||
HAVING
|
||||
SUM(CASE WHEN ip_tag <> '正常' THEN cnt ELSE 0 END) > 0
|
||||
) AS subquery`
|
||||
|
||||
// 获取总记录数参数
|
||||
countParams := []interface{}{global.GWAF_TENANT_ID, global.GWAF_USER_CODE}
|
||||
if req.Rule != "" {
|
||||
countParams = append(countParams, req.Rule)
|
||||
}
|
||||
if req.SrcIp != "" {
|
||||
countParams = append(countParams, req.SrcIp)
|
||||
}
|
||||
|
||||
// 执行记录数查询
|
||||
if err := global.GWAF_LOCAL_DB.Raw(countQuery, countParams...).Scan(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return results, total, nil
|
||||
}
|
||||
|
||||
// GetAllAttackIPTagListApi 获取所有攻击Tag
|
||||
func (receiver *WafLogService) GetAllAttackIPTagListApi() ([]model.AllIPTag, error) {
|
||||
var results []model.AllIPTag
|
||||
|
||||
// 基础查询部分
|
||||
query := `
|
||||
SELECT
|
||||
ip_tag as value,
|
||||
ip_tag || ' (' || sum(cnt) || ')' as label
|
||||
FROM
|
||||
"ip_tags"
|
||||
WHERE ip_tag<>'正常' and tenant_id=? and user_code=?
|
||||
GROUP BY
|
||||
tenant_id,
|
||||
ip_tag
|
||||
order by sum(cnt) desc
|
||||
`
|
||||
|
||||
// 构建查询参数
|
||||
params := []interface{}{global.GWAF_TENANT_ID, global.GWAF_USER_CODE}
|
||||
|
||||
// 执行查询
|
||||
if err := global.GWAF_LOCAL_DB.Raw(query, params...).Scan(&results).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
判断是否合法
|
||||
|
||||
@@ -425,3 +425,14 @@ func CheckSSLCertificateExpiry(host string) (time.Time, error) {
|
||||
// 返回到期时间
|
||||
return expiryDate, nil
|
||||
}
|
||||
|
||||
// GetSourceClientIP 获取原始IP
|
||||
func GetSourceClientIP(ipAndPort string) string {
|
||||
ip, _, err := net.SplitHostPort(ipAndPort)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return ip
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -226,6 +226,9 @@ func (waf *WafEngine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
RISK_LEVEL: 0, //危险等级
|
||||
GUEST_IDENTIFICATION: "正常访客", //访客身份识别
|
||||
TimeSpent: 0,
|
||||
NetSrcIp: utils.GetSourceClientIP(r.RemoteAddr),
|
||||
SrcByteBody: bodyByte,
|
||||
WebLogVersion: global.GWEBLOG_VERSION,
|
||||
}
|
||||
|
||||
formValues := url.Values{}
|
||||
@@ -343,6 +346,10 @@ func (waf *WafEngine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
}
|
||||
//如果正常的流量不记录请求原始包
|
||||
if global.GCONFIG_RECORD_ALL_SRC_BYTE_INFO == 0 {
|
||||
weblogbean.SrcByteBody = nil
|
||||
}
|
||||
//基本验证是否开关是否开启
|
||||
if hostTarget.Host.IsEnableHttpAuthBase == 1 {
|
||||
bHttpAuthBaseResult, sHttpAuthBaseResult := waf.DoHttpAuthBase(hostTarget, w, r)
|
||||
@@ -436,6 +443,8 @@ func (waf *WafEngine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
RISK_LEVEL: 1, //危险等级
|
||||
GUEST_IDENTIFICATION: "未解析域名", //访客身份识别
|
||||
TimeSpent: 0,
|
||||
NetSrcIp: utils.GetSourceClientIP(r.RemoteAddr),
|
||||
WebLogVersion: global.GWEBLOG_VERSION,
|
||||
}
|
||||
|
||||
//记录响应body
|
||||
@@ -613,7 +622,7 @@ func (waf *WafEngine) modifyResponse() func(*http.Response) error {
|
||||
}
|
||||
//文本资源
|
||||
if respContentType == "text/html" || respContentType == "application/html" ||
|
||||
respContentType == "application/json" || respContentType == "application/xml" {
|
||||
respContentType == "application/json" || respContentType == "application/xml" || respContentType == "text/plain" {
|
||||
contentType = "text"
|
||||
}
|
||||
//记录静态日志
|
||||
@@ -650,6 +659,7 @@ func (waf *WafEngine) modifyResponse() func(*http.Response) error {
|
||||
resp.Header.Set("Content-Length", strconv.FormatInt(int64(len(finalCompressBytes)), 10))
|
||||
if resp.ContentLength < global.GCONFIG_RECORD_MAX_RES_BODY_LENGTH {
|
||||
weblogfrist.RES_BODY = string(orgContentBytes)
|
||||
weblogfrist.SrcByteResBody = orgContentBytes
|
||||
}
|
||||
}
|
||||
zlog.Debug("TESTChanllage", weblogfrist.HOST, weblogfrist.URL)
|
||||
|
||||
@@ -51,6 +51,9 @@ func setConfigIntValue(name string, value int64, change int) {
|
||||
case "keepalive_time_out":
|
||||
global.GCONFIG_RECORD_KEEPALIVE_TIME_OUT = value
|
||||
break
|
||||
case "record_all_src_byte_info":
|
||||
global.GCONFIG_RECORD_ALL_SRC_BYTE_INFO = value
|
||||
break
|
||||
default:
|
||||
zlog.Warn("Unknown config item:", name)
|
||||
}
|
||||
@@ -158,4 +161,6 @@ func TaskLoadSetting(initLoad bool) {
|
||||
updateConfigIntItem(initLoad, "network", "connect_time_out", global.GCONFIG_RECORD_CONNECT_TIME_OUT, "连接超时(默认30s)", "int", "")
|
||||
updateConfigIntItem(initLoad, "network", "keepalive_time_out", global.GCONFIG_RECORD_KEEPALIVE_TIME_OUT, "保持活动超时(默认30s)", "int", "")
|
||||
|
||||
updateConfigIntItem(initLoad, "system", "record_all_src_byte_info", global.GCONFIG_RECORD_ALL_SRC_BYTE_INFO, "启动记录原始请求BODY报文(1启动 0关闭)", "int", "")
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user