diff --git a/internal/waf/action_js_cookie.go b/internal/waf/action_js_cookie.go
new file mode 100644
index 0000000..3c62f07
--- /dev/null
+++ b/internal/waf/action_js_cookie.go
@@ -0,0 +1,128 @@
+// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
+
+package waf
+
+import (
+ "crypto/md5"
+ "fmt"
+ "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
+ "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
+ "github.com/TeaOSLab/EdgeNode/internal/ttlcache"
+ "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
+ "github.com/iwind/TeaGo/types"
+ "net/http"
+ "time"
+)
+
+type JSCookieAction struct {
+ BaseAction
+
+ Life int32 `yaml:"life" json:"life"`
+ MaxFails int `yaml:"maxFails" json:"maxFails"` // 最大失败次数
+ FailBlockTimeout int `yaml:"failBlockTimeout" json:"failBlockTimeout"` // 失败拦截时间
+ Scope string `yaml:"scope" json:"scope"`
+}
+
+func (this *JSCookieAction) Init(waf *WAF) error {
+ this.Scope = firewallconfigs.FirewallScopeGlobal
+
+ return nil
+}
+
+func (this *JSCookieAction) Code() string {
+ return ActionJavascriptCookie
+}
+
+func (this *JSCookieAction) IsAttack() bool {
+ return false
+}
+
+func (this *JSCookieAction) WillChange() bool {
+ return true
+}
+
+func (this *JSCookieAction) Perform(waf *WAF, group *RuleGroup, set *RuleSet, req requests.Request, writer http.ResponseWriter) (allow bool) {
+ // 是否在白名单中
+ if SharedIPWhiteList.Contains("set:"+types.String(set.Id), this.Scope, req.WAFServerId(), req.WAFRemoteIP()) {
+ return true
+ }
+
+ nodeConfig, err := nodeconfigs.SharedNodeConfig()
+ if err != nil {
+ return true
+ }
+
+ var life = this.Life
+ if life <= 0 {
+ life = 3600
+ }
+
+ // 检查Cookie
+ var cookieName = "ge_js_validator_" + types.String(set.Id)
+ cookie, err := req.WAFRaw().Cookie(cookieName)
+ if err == nil && cookie != nil {
+ var cookieValue = cookie.Value
+ if len(cookieValue) > 10 {
+ var timestamp = cookieValue[:10]
+ if types.Int64(timestamp) >= time.Now().Unix()-int64(life) && fmt.Sprintf("%x", md5.Sum([]byte(timestamp+"@"+nodeConfig.NodeId))) == cookieValue[10:] {
+ return true
+ }
+ }
+ }
+
+ writer.Header().Set("Content-Type", "text/html; charset=utf-8")
+ writer.Header().Set("Cache-Control", "no-cache")
+
+ var timestamp = types.String(time.Now().Unix())
+
+ var cookieValue = timestamp + fmt.Sprintf("%x", md5.Sum([]byte(timestamp+"@"+nodeConfig.NodeId)))
+
+ _, _ = writer.Write([]byte(`
+
+
+
+
+
+
+
+
+`))
+
+ // 记录失败次数
+ this.increaseFails(req, waf.Id, group.Id, set.Id)
+
+ return false
+}
+
+func (this *JSCookieAction) increaseFails(req requests.Request, policyId int64, groupId int64, setId int64) (goNext bool) {
+ var maxFails = this.MaxFails
+ var failBlockTimeout = this.FailBlockTimeout
+
+ if maxFails <= 0 {
+ maxFails = 10 // 默认10次
+ } else if maxFails <= 3 {
+ maxFails = 3 // 不能小于3,防止意外刷新出现
+ }
+ if failBlockTimeout <= 0 {
+ failBlockTimeout = 1800 // 默认1800s
+ }
+
+ var key = "JS_COOKIE:FAILS:" + req.WAFRemoteIP() + ":" + types.String(req.WAFServerId())
+
+ var countFails = ttlcache.SharedCache.IncreaseInt64(key, 1, time.Now().Unix()+300, true)
+ if int(countFails) >= maxFails {
+ var useLocalFirewall = false
+
+ if this.Scope == firewallconfigs.FirewallScopeGlobal {
+ useLocalFirewall = true
+ }
+
+ SharedIPBlackList.RecordIP(IPTypeAll, firewallconfigs.FirewallScopeService, req.WAFServerId(), req.WAFRemoteIP(), time.Now().Unix()+int64(failBlockTimeout), policyId, useLocalFirewall, groupId, setId, "JS_COOKIE验证连续失败超过"+types.String(maxFails)+"次")
+ return false
+ }
+
+ return true
+}
diff --git a/internal/waf/action_types.go b/internal/waf/action_types.go
index 75aeace..2ce8778 100644
--- a/internal/waf/action_types.go
+++ b/internal/waf/action_types.go
@@ -5,18 +5,19 @@ import "reflect"
type ActionString = string
const (
- ActionLog ActionString = "log" // allow and log
- ActionBlock ActionString = "block" // block
- ActionCaptcha ActionString = "captcha" // block and show captcha
- ActionNotify ActionString = "notify" // 告警
- ActionGet302 ActionString = "get_302" // 针对GET的302重定向认证
- ActionPost307 ActionString = "post_307" // 针对POST的307重定向认证
- ActionRecordIP ActionString = "record_ip" // 记录IP
- ActionTag ActionString = "tag" // 标签
- ActionPage ActionString = "page" // 显示网页
- ActionAllow ActionString = "allow" // allow
- ActionGoGroup ActionString = "go_group" // go to next rule group
- ActionGoSet ActionString = "go_set" // go to next rule set
+ ActionLog ActionString = "log" // allow and log
+ ActionBlock ActionString = "block" // block
+ ActionCaptcha ActionString = "captcha" // block and show captcha
+ ActionJavascriptCookie ActionString = "js_cookie" // js cookie
+ ActionNotify ActionString = "notify" // 告警
+ ActionGet302 ActionString = "get_302" // 针对GET的302重定向认证
+ ActionPost307 ActionString = "post_307" // 针对POST的307重定向认证
+ ActionRecordIP ActionString = "record_ip" // 记录IP
+ ActionTag ActionString = "tag" // 标签
+ ActionPage ActionString = "page" // 显示网页
+ ActionAllow ActionString = "allow" // allow
+ ActionGoGroup ActionString = "go_group" // go to next rule group
+ ActionGoSet ActionString = "go_set" // go to next rule set
)
var AllActions = []*ActionDefinition{
@@ -44,6 +45,12 @@ var AllActions = []*ActionDefinition{
Instance: new(CaptchaAction),
Type: reflect.TypeOf(new(CaptchaAction)).Elem(),
},
+ {
+ Name: "JS Cookie验证",
+ Code: ActionJavascriptCookie,
+ Instance: new(JSCookieAction),
+ Type: reflect.TypeOf(new(JSCookieAction)).Elem(),
+ },
{
Name: "告警",
Code: ActionNotify,
diff --git a/internal/waf/captcha_counter.go b/internal/waf/captcha_counter.go
index 7007bc3..8a974d1 100644
--- a/internal/waf/captcha_counter.go
+++ b/internal/waf/captcha_counter.go
@@ -34,7 +34,7 @@ func CaptchaIncreaseFails(req requests.Request, actionConfig *CaptchaAction, pol
useLocalFirewall = true
}
- SharedIPBlackList.RecordIP(IPTypeAll, firewallconfigs.FirewallScopeService, req.WAFServerId(), req.WAFRemoteIP(), time.Now().Unix()+int64(failBlockTimeout), policyId, useLocalFirewall, groupId, setId, "CAPTCHA验证连续失败")
+ SharedIPBlackList.RecordIP(IPTypeAll, firewallconfigs.FirewallScopeService, req.WAFServerId(), req.WAFRemoteIP(), time.Now().Unix()+int64(failBlockTimeout), policyId, useLocalFirewall, groupId, setId, "CAPTCHA验证连续失败超过"+types.String(maxFails)+"次")
return false
}
}