feat:add focus attack log

#24
This commit is contained in:
samwaf
2025-01-23 13:53:00 +08:00
parent f5a988e595
commit bedcbf5c04
11 changed files with 226 additions and 1 deletions

View File

@@ -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")

View File

@@ -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 //记录原始信息(默认不开启)
)

View File

@@ -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证书验证路径

View File

@@ -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 方法中定义复合索引

View File

@@ -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"`
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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
}
/*
*
判断是否合法

View File

@@ -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
}
}

View File

@@ -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)

View File

@@ -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", "")
}