Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions api/waf_system_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,30 @@ func (w *WafSystemConfigApi) ModifyApi(c *gin.Context) {
response.FailWithMessage("解析失败", c)
}
}

// ModifyByItemApi 通过 item 修改系统配置的 value
func (w *WafSystemConfigApi) ModifyByItemApi(c *gin.Context) {
var req request.WafSystemConfigEditByItemReq
err := c.ShouldBindJSON(&req)
if err != nil {
response.FailWithMessage("解析失败", c)
return
}

if req.Item == "" {
response.FailWithMessage("item 不能为空", c)
return
}

err = wafSystemConfigService.ModifyByItemApi(req)
if err != nil {
response.FailWithMessage("编辑发生错误: "+err.Error(), c)
} else {
waftask.TaskLoadSetting(true)
response.OkWithMessage("编辑成功", c)
}
}

func (w *WafSystemConfigApi) GetDetailByItemApi(c *gin.Context) {
var req request.WafSystemConfigDetailByItemReq
err := c.ShouldBind(&req)
Expand Down
4 changes: 4 additions & 0 deletions model/request/waf_system_config_req.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ type WafSystemConfigSearchReq struct {
type WafSystemConfigDetailByItemReq struct {
Item string `json:"item" form:"item"` //item
}
type WafSystemConfigEditByItemReq struct {
Item string `json:"item" form:"item"` //item
Value string `json:"value" form:"value"` //value
}
1 change: 1 addition & 0 deletions router/waf_system_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ func (receiver *SystemConfigRouter) InitSystemConfigRouter(group *gin.RouterGrou
router.POST("/api/v1/systemconfig/add", api.AddApi)
router.GET("/api/v1/systemconfig/del", api.DelApi)
router.POST("/api/v1/systemconfig/edit", api.ModifyApi)
router.POST("/api/v1/systemconfig/editByItem", api.ModifyByItemApi)
}
19 changes: 19 additions & 0 deletions service/waf_service/waf_system_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,25 @@ func (receiver *WafSystemConfigService) ModifyApi(req request.WafSystemConfigEdi

return err
}

// ModifyByItemApi 通过 item 修改系统配置的 value
func (receiver *WafSystemConfigService) ModifyByItemApi(req request.WafSystemConfigEditByItemReq) error {
var sysConfig model.SystemConfig
err := global.GWAF_LOCAL_DB.Where("item = ?", req.Item).First(&sysConfig).Error
if err != nil {
return errors.New("配置项不存在")
}

editMap := map[string]interface{}{
"Value": req.Value,
"UPDATE_TIME": customtype.JsonTime(time.Now()),
}

err = global.GWAF_LOCAL_DB.Model(model.SystemConfig{}).Where("item = ?", req.Item).Updates(editMap).Error

return err
}

func (receiver *WafSystemConfigService) GetDetailApi(req request.WafSystemConfigDetailReq) model.SystemConfig {
var bean model.SystemConfig
global.GWAF_LOCAL_DB.Where("id=?", req.Id).Find(&bean)
Expand Down
155 changes: 134 additions & 21 deletions wafenginecore/wafengine.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,10 @@ type WafEngine struct {
}

func (waf *WafEngine) Error() string {
fs := "HTTP: %d, HostCode: %d, Message: %s"
zlog.Error(fmt.Sprintf(fs))
return fmt.Sprintf(fs)
// 返回错误信息的固定格式
const errFormat = "WafEngine error occurred"
zlog.Error(errFormat)
return errFormat
}

// inferAttackType 根据检测规则标题推断攻击类型
Expand Down Expand Up @@ -440,26 +441,23 @@ func (waf *WafEngine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}

if host == hostTarget.Host.Host+":80" && !strings.HasPrefix(weblogbean.URL, global.GSSL_HTTP_CHANGLE_PATH) && hostTarget.Host.AutoJumpHTTPS == 1 && hostTarget.Host.Ssl == 1 {
domainJump := ""
if strings.Contains(host, ":") {
partsJump := strings.Split(host, ":")
if len(partsJump) == 2 {
domainJump = partsJump[0]
if r.TLS == nil {
// 检查是否需要自动跳转到HTTPS
shouldJump, domainJump := shouldAutoJumpHTTPS(host, hostTarget.Host.Host, weblogbean.URL, hostTarget.Host.AutoJumpHTTPS, hostTarget.Host.Ssl)
if shouldJump {
// 重定向到 HTTPS 版本的 URL
targetHttpsUrl := fmt.Sprintf("%s%s%s", "https://", domainJump, r.URL.Path)
// 只有在非标准端口时才显示端口号(443是标准端口)
if hostTarget.Host.Port != 443 {
targetHttpsUrl = fmt.Sprintf("%s%s:%d%s", "https://", domainJump, hostTarget.Host.Port, r.URL.Path)
}
if r.URL.RawQuery != "" {
targetHttpsUrl += "?" + r.URL.RawQuery
}
zlog.Info(innerLogName, "jump https", targetHttpsUrl)
http.Redirect(w, r, targetHttpsUrl, int(global.GCONFIG_RECORD_REDIRECT_HTTPS_CODE))
return
}
// 重定向到 HTTPS 版本的 URL
targetHttpsUrl := fmt.Sprintf("%s%s%s", "https://", domainJump, r.URL.Path)
// 只有在非标准端口时才显示端口号(443是标准端口)
if hostTarget.Host.Port != 443 {
targetHttpsUrl = fmt.Sprintf("%s%s:%d%s", "https://", domainJump, hostTarget.Host.Port, r.URL.Path)
}
if r.URL.RawQuery != "" {
targetHttpsUrl += "?" + r.URL.RawQuery
}
zlog.Info(innerLogName, "jump https", targetHttpsUrl)
http.Redirect(w, r, targetHttpsUrl, int(global.GCONFIG_RECORD_REDIRECT_HTTPS_CODE))
return
}

r.Header.Add("waf_req_uuid", weblogbean.REQ_UUID)
Expand Down Expand Up @@ -1817,3 +1815,118 @@ func (waf *WafEngine) checkWithPlugins(r *http.Request, weblogbean *innerbean.We
// 插件检查通过,不拦截
return detection.Result{IsBlock: false}
}

// shouldAutoJumpHTTPS 判断是否应该自动跳转到HTTPS
// 参数:
// - requestHost: 请求的host(如: aaa.samwaf.com:80 或 bbb.aaa.samwaf.com:8080)
// - configHost: 配置的host(如: *.samwaf.com 或 example.com)
// - requestURL: 请求的URL路径
// - autoJumpHTTPS: 是否开启自动跳转HTTPS
// - ssl: 是否启用了SSL
//
// 返回:
// - bool: 是否应该跳转
// - string: 跳转的域名部分(不含端口)
//
// 支持场景:
// - 精确匹配: example.com:80 匹配 example.com
// - 二级泛域名: aaa.samwaf.com:80 匹配 *.samwaf.com
// - 多级泛域名: bbb.aaa.samwaf.com:8080 匹配 *.samwaf.com (利用MaskSubdomain)
func shouldAutoJumpHTTPS(requestHost, configHost, requestURL string, autoJumpHTTPS, ssl int) (bool, string) {
// 检查基本条件:必须开启自动跳转和SSL
if autoJumpHTTPS != 1 || ssl != 1 {
return false, ""
}

// 排除SSL证书验证路径
if strings.HasPrefix(requestURL, global.GSSL_HTTP_CHANGLE_PATH) {
return false, ""
}

// 从请求host中提取域名和端口
requestDomain := ""
requestPort := ""
if strings.Contains(requestHost, ":") {
parts := strings.Split(requestHost, ":")
if len(parts) == 2 {
requestDomain = parts[0]
requestPort = parts[1]
}
} else {
requestDomain = requestHost
}

// 判断是否是HTTP端口(80或其他非443端口)
// 如果没有端口号或者端口是443,不需要跳转
if requestPort == "" || requestPort == "443" {
return false, ""
}

// 检查域名是否匹配
matched := false

// 情况1: 精确匹配(如: example.com 匹配 example.com)
if requestDomain == configHost {
matched = true
}

// 情况2: 配置host带端口的精确匹配(如: example.com:80 匹配 example.com:80)
if !matched && strings.Contains(configHost, ":") {
if requestHost == configHost {
matched = true
}
}

// 情况3: 泛域名匹配(支持多级域名)
// 例如: aaa.samwaf.com:80 匹配 *.samwaf.com
// bbb.aaa.samwaf.com:80 匹配 *.samwaf.com
// ccc.bbb.aaa.samwaf.com:8080 匹配 *.samwaf.com
if !matched && strings.HasPrefix(configHost, "*.") {
// 方法1: 直接匹配(二级域名)
// 提取主域名部分(去掉*.)
baseDomain := configHost[2:] // 去掉 "*."
// 检查请求域名是否以主域名结尾
if strings.HasSuffix(requestDomain, baseDomain) {
// 确保是完整的子域名匹配,而不是部分匹配
// 例如: aaa.samwaf.com 匹配 *.samwaf.com
// 但 aaasamwaf.com 不匹配 *.samwaf.com
if requestDomain != baseDomain && strings.HasSuffix(requestDomain, "."+baseDomain) {
matched = true
}
}

// 方法2: 使用MaskSubdomain处理多级域名
// 例如: bbb.aaa.samwaf.com:80 -> *.aaa.samwaf.com:80
// 然后递归检查 *.aaa.samwaf.com 是否匹配 *.samwaf.com
if !matched {
maskedHost := domaintool.MaskSubdomain(requestHost)
// maskedHost 示例: *.aaa.samwaf.com:80
// 如果 maskedHost 和 requestHost 不同,说明有多级域名
if maskedHost != requestHost {
// 提取 maskedHost 的域名部分(去掉端口)
maskedDomain := maskedHost
if strings.Contains(maskedHost, ":") {
maskedDomain = strings.Split(maskedHost, ":")[0]
}

// 递归检查: *.aaa.samwaf.com 是否匹配 *.samwaf.com
// 即检查 aaa.samwaf.com 是否匹配 *.samwaf.com 的baseDomain
if strings.HasPrefix(maskedDomain, "*.") {
checkDomain := maskedDomain[2:] // 去掉 "*.",得到 aaa.samwaf.com
// 检查 aaa.samwaf.com 是否匹配 *.samwaf.com
if strings.HasSuffix(checkDomain, baseDomain) {
if checkDomain != baseDomain && strings.HasSuffix(checkDomain, "."+baseDomain) {
matched = true
}
}
}
}
}
}

if matched {
return true, requestDomain
}

return false, ""
}
Loading
Loading