github.com/TeaOSLab/EdgeNode@v1.3.8/internal/waf/action_js_cookie.go (about) 1 // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn . 2 3 package waf 4 5 import ( 6 "crypto/md5" 7 "fmt" 8 "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" 9 "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs" 10 "github.com/TeaOSLab/EdgeNode/internal/utils/counters" 11 "github.com/TeaOSLab/EdgeNode/internal/waf/requests" 12 "github.com/iwind/TeaGo/types" 13 "net/http" 14 "strings" 15 "time" 16 ) 17 18 type JSCookieAction struct { 19 BaseAction 20 21 Life int32 `yaml:"life" json:"life"` 22 MaxFails int `yaml:"maxFails" json:"maxFails"` // 最大失败次数 23 FailBlockTimeout int `yaml:"failBlockTimeout" json:"failBlockTimeout"` // 失败拦截时间 24 Scope string `yaml:"scope" json:"scope"` 25 26 FailBlockScopeAll bool `yaml:"failBlockScopeAll" json:"failBlockScopeAll"` 27 } 28 29 func (this *JSCookieAction) Init(waf *WAF) error { 30 31 if waf.DefaultJSCookieAction != nil { 32 if this.Life <= 0 { 33 this.Life = waf.DefaultJSCookieAction.Life 34 } 35 if this.MaxFails <= 0 { 36 this.MaxFails = waf.DefaultJSCookieAction.MaxFails 37 } 38 if this.FailBlockTimeout <= 0 { 39 this.FailBlockTimeout = waf.DefaultJSCookieAction.FailBlockTimeout 40 } 41 if len(this.Scope) == 0 { 42 this.Scope = waf.DefaultJSCookieAction.Scope 43 } 44 45 this.FailBlockScopeAll = waf.DefaultJSCookieAction.FailBlockScopeAll 46 } 47 48 if len(this.Scope) == 0 { 49 this.Scope = firewallconfigs.FirewallScopeGlobal 50 } 51 52 return nil 53 } 54 55 func (this *JSCookieAction) Code() string { 56 return ActionJavascriptCookie 57 } 58 59 func (this *JSCookieAction) IsAttack() bool { 60 return false 61 } 62 63 func (this *JSCookieAction) WillChange() bool { 64 return true 65 } 66 67 func (this *JSCookieAction) Perform(waf *WAF, group *RuleGroup, set *RuleSet, req requests.Request, writer http.ResponseWriter) PerformResult { 68 // 是否在白名单中 69 if SharedIPWhiteList.Contains("set:"+types.String(set.Id), this.Scope, req.WAFServerId(), req.WAFRemoteIP()) { 70 return PerformResult{ 71 ContinueRequest: true, 72 } 73 } 74 75 nodeConfig, err := nodeconfigs.SharedNodeConfig() 76 if err != nil { 77 return PerformResult{ 78 ContinueRequest: true, 79 } 80 } 81 82 var life = this.Life 83 if life <= 0 { 84 life = 3600 85 } 86 87 // 检查Cookie 88 var cookieName = "ge_js_validator_" + types.String(set.Id) 89 cookie, err := req.WAFRaw().Cookie(cookieName) 90 if err == nil && cookie != nil { 91 var cookieValue = cookie.Value 92 if len(cookieValue) > 10 { 93 var pieces = strings.Split(cookieValue, "@") 94 if len(pieces) == 3 { 95 var timestamp = pieces[0] 96 var sum = pieces[2] 97 if types.Int64(timestamp) >= time.Now().Unix()-int64(life) && fmt.Sprintf("%x", md5.Sum([]byte(timestamp+"@"+types.String(set.Id)+"@"+nodeConfig.NodeId))) == sum { 98 return PerformResult{ 99 ContinueRequest: true, 100 } 101 } 102 } 103 } 104 } 105 106 req.ProcessResponseHeaders(writer.Header(), http.StatusOK) 107 108 writer.Header().Set("Content-Type", "text/html; charset=utf-8") 109 writer.Header().Set("Cache-Control", "no-cache") 110 111 var timestamp = types.String(time.Now().Unix()) 112 113 var cookieValue = timestamp + "@" + types.String(set.Id) + "@" + fmt.Sprintf("%x", md5.Sum([]byte(timestamp+"@"+types.String(set.Id)+"@"+nodeConfig.NodeId))) 114 var respHTML = `<!DOCTYPE html> 115 <html> 116 <head> 117 <title></title> 118 <meta charset="UTF-8"/> 119 <script type="text/javascript"> 120 document.cookie = "` + cookieName + `=` + cookieValue + `; path=/; max-age=` + types.String(life) + `;"; 121 window.location.reload(); 122 </script> 123 </head> 124 <body> 125 </body> 126 </html>` 127 writer.Header().Set("Content-Length", types.String(len(respHTML))) 128 writer.WriteHeader(http.StatusOK) 129 _, _ = writer.Write([]byte(respHTML)) 130 131 // 记录失败次数 132 this.increaseFails(req, waf.Id, group.Id, set.Id, waf.UseLocalFirewall && (this.FailBlockScopeAll || this.Scope == firewallconfigs.FirewallScopeGlobal)) 133 134 return PerformResult{} 135 } 136 137 func (this *JSCookieAction) increaseFails(req requests.Request, policyId int64, groupId int64, setId int64, useLocalFirewall bool) (goNext bool) { 138 var maxFails = this.MaxFails 139 var failBlockTimeout = this.FailBlockTimeout 140 141 if maxFails <= 0 { 142 maxFails = 10 // 默认10次 143 } else if maxFails <= 5 { 144 maxFails = 5 // 不能小于3,防止意外刷新出现 145 } 146 if failBlockTimeout <= 0 { 147 failBlockTimeout = 1800 // 默认1800s 148 } 149 150 var key = "WAF:JS_COOKIE:FAILS:" + req.WAFRemoteIP() + ":" + types.String(req.WAFServerId()) + ":" + req.WAFRaw().URL.String() 151 152 var countFails = counters.SharedCounter.IncreaseKey(key, 300) 153 if int(countFails) >= maxFails { 154 SharedIPBlackList.RecordIP(IPTypeAll, firewallconfigs.FirewallScopeServer, req.WAFServerId(), req.WAFRemoteIP(), time.Now().Unix()+int64(failBlockTimeout), policyId, useLocalFirewall, groupId, setId, "JS_COOKIE验证连续失败超过"+types.String(maxFails)+"次") 155 return false 156 } 157 158 return true 159 }