mirror of
https://github.com/TeaOSLab/EdgeCommon.git
synced 2025-11-07 15:20:24 +08:00
[waf]支持包含二进制、不支持二进制等操作符;支持对参数值编解码
This commit is contained in:
21
pkg/serverconfigs/filterconfigs/filter_base64_decode.go
Normal file
21
pkg/serverconfigs/filterconfigs/filter_base64_decode.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package filterconfigs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Base64DecodeFilter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Base64DecodeFilter) Init() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Base64DecodeFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) {
|
||||||
|
output, err = base64.StdEncoding.DecodeString(ToString(input))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
goNext = true
|
||||||
|
return
|
||||||
|
}
|
||||||
14
pkg/serverconfigs/filterconfigs/filter_base64_decode_test.go
Normal file
14
pkg/serverconfigs/filterconfigs/filter_base64_decode_test.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package filterconfigs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBase64DecodeFilter_Do(t *testing.T) {
|
||||||
|
filter := &Base64DecodeFilter{}
|
||||||
|
t.Log(filter.Do("123456", nil))
|
||||||
|
|
||||||
|
encodedString := base64.StdEncoding.EncodeToString([]byte("hello"))
|
||||||
|
t.Log(filter.Do(encodedString, nil))
|
||||||
|
}
|
||||||
19
pkg/serverconfigs/filterconfigs/filter_base64_encode.go
Normal file
19
pkg/serverconfigs/filterconfigs/filter_base64_encode.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package filterconfigs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Base64EncodeFilter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Base64EncodeFilter) Init() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Base64EncodeFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) {
|
||||||
|
data := ToBytes(input)
|
||||||
|
output = base64.StdEncoding.EncodeToString(data)
|
||||||
|
goNext = true
|
||||||
|
return
|
||||||
|
}
|
||||||
28
pkg/serverconfigs/filterconfigs/filter_base64_encode_test.go
Normal file
28
pkg/serverconfigs/filterconfigs/filter_base64_encode_test.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package filterconfigs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"github.com/iwind/TeaGo/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBase64EncodeFilter_Do(t *testing.T) {
|
||||||
|
a := assert.NewAssertion(t)
|
||||||
|
|
||||||
|
filter := &Base64EncodeFilter{}
|
||||||
|
t.Log(filter.Do("hello", nil))
|
||||||
|
t.Log(filter.Do("=", nil))
|
||||||
|
|
||||||
|
output, goNext, err := filter.Do("123456", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
a.IsTrue(goNext)
|
||||||
|
|
||||||
|
outputString := output.(string)
|
||||||
|
result, err := base64.StdEncoding.DecodeString(outputString)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("origin:", string(result))
|
||||||
|
}
|
||||||
10
pkg/serverconfigs/filterconfigs/filter_interface.go
Normal file
10
pkg/serverconfigs/filterconfigs/filter_interface.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package filterconfigs
|
||||||
|
|
||||||
|
// 过滤接口
|
||||||
|
type FilterInterface interface {
|
||||||
|
// 初始化
|
||||||
|
Init() error
|
||||||
|
|
||||||
|
// 执行过滤
|
||||||
|
Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error)
|
||||||
|
}
|
||||||
16
pkg/serverconfigs/filterconfigs/filter_length.go
Normal file
16
pkg/serverconfigs/filterconfigs/filter_length.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package filterconfigs
|
||||||
|
|
||||||
|
type LengthFilter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
func (this *LengthFilter) Init() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行过滤
|
||||||
|
func (this *LengthFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) {
|
||||||
|
output = len(ToBytes(input))
|
||||||
|
goNext = true
|
||||||
|
return
|
||||||
|
}
|
||||||
9
pkg/serverconfigs/filterconfigs/filter_length_test.go
Normal file
9
pkg/serverconfigs/filterconfigs/filter_length_test.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package filterconfigs
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestLengthFilter_Do(t *testing.T) {
|
||||||
|
filter := &LengthFilter{}
|
||||||
|
t.Log(filter.Do("hello", nil))
|
||||||
|
t.Log(filter.Do([]byte("hello"), nil))
|
||||||
|
}
|
||||||
23
pkg/serverconfigs/filterconfigs/filter_md5.go
Normal file
23
pkg/serverconfigs/filterconfigs/filter_md5.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package filterconfigs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Md5Filter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Md5Filter) Init() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Md5Filter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) {
|
||||||
|
data := ToBytes(input)
|
||||||
|
m := md5.New()
|
||||||
|
m.Write(data)
|
||||||
|
result := m.Sum(nil)
|
||||||
|
output = fmt.Sprintf("%x", result)
|
||||||
|
goNext = true
|
||||||
|
return
|
||||||
|
}
|
||||||
11
pkg/serverconfigs/filterconfigs/filter_md5_test.go
Normal file
11
pkg/serverconfigs/filterconfigs/filter_md5_test.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package filterconfigs
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestMd5Filter_Do(t *testing.T) {
|
||||||
|
filter := &Md5Filter{}
|
||||||
|
t.Log(filter.Do("123456", nil))
|
||||||
|
t.Log(filter.Do(nil, nil))
|
||||||
|
t.Log(filter.Do("", nil))
|
||||||
|
t.Log(filter.Do("hello", nil))
|
||||||
|
}
|
||||||
19
pkg/serverconfigs/filterconfigs/filter_urldecode.go
Normal file
19
pkg/serverconfigs/filterconfigs/filter_urldecode.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package filterconfigs
|
||||||
|
|
||||||
|
import "net/url"
|
||||||
|
|
||||||
|
type URLDecodeFilter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *URLDecodeFilter) Init() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *URLDecodeFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) {
|
||||||
|
output, err = url.QueryUnescape(ToString(input))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
goNext = true
|
||||||
|
return
|
||||||
|
}
|
||||||
13
pkg/serverconfigs/filterconfigs/filter_urldecode_test.go
Normal file
13
pkg/serverconfigs/filterconfigs/filter_urldecode_test.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package filterconfigs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestURLDecodeFilter_Do(t *testing.T) {
|
||||||
|
filter := &URLDecodeFilter{}
|
||||||
|
t.Log(filter.Do("hello", nil))
|
||||||
|
t.Log(filter.Do(url.QueryEscape("/hello/world/?a=b&c=d"), nil))
|
||||||
|
t.Log(filter.Do("/hello/world/?a=b&c=d", nil))
|
||||||
|
}
|
||||||
16
pkg/serverconfigs/filterconfigs/filter_urlencode.go
Normal file
16
pkg/serverconfigs/filterconfigs/filter_urlencode.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package filterconfigs
|
||||||
|
|
||||||
|
import "net/url"
|
||||||
|
|
||||||
|
type URLEncodeFilter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *URLEncodeFilter) Init() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *URLEncodeFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) {
|
||||||
|
output = url.QueryEscape(ToString(input))
|
||||||
|
goNext = true
|
||||||
|
return
|
||||||
|
}
|
||||||
9
pkg/serverconfigs/filterconfigs/filter_urlencode_test.go
Normal file
9
pkg/serverconfigs/filterconfigs/filter_urlencode_test.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package filterconfigs
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestURLEncodeFilter_Do(t *testing.T) {
|
||||||
|
filter := &URLEncodeFilter{}
|
||||||
|
t.Log(filter.Do("hello", nil))
|
||||||
|
t.Log(filter.Do("/hello/world?a=b&c=中文&d=<symbol>", nil))
|
||||||
|
}
|
||||||
27
pkg/serverconfigs/filterconfigs/filters.go
Normal file
27
pkg/serverconfigs/filterconfigs/filters.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package filterconfigs
|
||||||
|
|
||||||
|
import "github.com/iwind/TeaGo/logs"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for code, filter := range allFilters {
|
||||||
|
err := filter.Init()
|
||||||
|
if err != nil {
|
||||||
|
logs.Println("[FILTER]init '" + code + "' failed: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 所有的筛选条件
|
||||||
|
var allFilters = map[string]FilterInterface{
|
||||||
|
"md5": new(Md5Filter),
|
||||||
|
"urlEncode": new(URLEncodeFilter),
|
||||||
|
"urlDecode": new(URLDecodeFilter),
|
||||||
|
"base64Encode": new(Base64EncodeFilter),
|
||||||
|
"base64Decode": new(Base64DecodeFilter),
|
||||||
|
"length": new(LengthFilter),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找Filter
|
||||||
|
func FindFilter(code string) FilterInterface {
|
||||||
|
return allFilters[code]
|
||||||
|
}
|
||||||
25
pkg/serverconfigs/filterconfigs/utils.go
Normal file
25
pkg/serverconfigs/filterconfigs/utils.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package filterconfigs
|
||||||
|
|
||||||
|
import "github.com/iwind/TeaGo/types"
|
||||||
|
|
||||||
|
// 将输入内容转换为字节
|
||||||
|
func ToBytes(input interface{}) []byte {
|
||||||
|
if input == nil {
|
||||||
|
return []byte{}
|
||||||
|
}
|
||||||
|
var data []byte
|
||||||
|
var ok bool
|
||||||
|
data, ok = input.([]byte)
|
||||||
|
if ok {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
return []byte(types.String(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将输入内容转换为字符串
|
||||||
|
func ToString(input interface{}) string {
|
||||||
|
if input == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return types.String(input)
|
||||||
|
}
|
||||||
9
pkg/serverconfigs/filterconfigs/utils_test.go
Normal file
9
pkg/serverconfigs/filterconfigs/utils_test.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package filterconfigs
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestToBytes(t *testing.T) {
|
||||||
|
t.Log(ToBytes("hello"))
|
||||||
|
t.Log(ToBytes(123))
|
||||||
|
t.Log(ToBytes([]byte{1, 2, 3}))
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ type HTTPFirewallCheckpointDefinition struct {
|
|||||||
Description string `json:"description"` // 描述
|
Description string `json:"description"` // 描述
|
||||||
Prefix string `json:"prefix"` // 前缀
|
Prefix string `json:"prefix"` // 前缀
|
||||||
IsRequest bool `json:"isRequest"` // 是否为请求
|
IsRequest bool `json:"isRequest"` // 是否为请求
|
||||||
|
HasParams bool `json:"hasParams"` // 是否有子参数
|
||||||
Params []*KeyValue `json:"params"` // 参数
|
Params []*KeyValue `json:"params"` // 参数
|
||||||
Options []OptionInterface `json:"options"` // 选项
|
Options []OptionInterface `json:"options"` // 选项
|
||||||
IsComposed bool `json:"isComposed"` // 是否为组合的checkpoint
|
IsComposed bool `json:"isComposed"` // 是否为组合的checkpoint
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ var AllCheckpoints = []*HTTPFirewallCheckpointDefinition{
|
|||||||
Prefix: "requestForm",
|
Prefix: "requestForm",
|
||||||
Description: "获取POST或者其他方法发送的表单参数,最大请求体限制32M",
|
Description: "获取POST或者其他方法发送的表单参数,最大请求体限制32M",
|
||||||
IsRequest: true,
|
IsRequest: true,
|
||||||
|
HasParams: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "上传文件",
|
Name: "上传文件",
|
||||||
@@ -93,12 +94,14 @@ var AllCheckpoints = []*HTTPFirewallCheckpointDefinition{
|
|||||||
NewKeyValue("表单字段名", "field"),
|
NewKeyValue("表单字段名", "field"),
|
||||||
},
|
},
|
||||||
IsRequest: true,
|
IsRequest: true,
|
||||||
|
HasParams: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "请求JSON参数",
|
Name: "请求JSON参数",
|
||||||
Prefix: "requestJSON",
|
Prefix: "requestJSON",
|
||||||
Description: "获取POST或者其他方法发送的JSON,最大请求体限制32M,使用点(.)符号表示多级数据",
|
Description: "获取POST或者其他方法发送的JSON,最大请求体限制32M,使用点(.)符号表示多级数据",
|
||||||
IsRequest: true,
|
IsRequest: true,
|
||||||
|
HasParams: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "请求方法",
|
Name: "请求方法",
|
||||||
@@ -153,6 +156,7 @@ var AllCheckpoints = []*HTTPFirewallCheckpointDefinition{
|
|||||||
Prefix: "cookie",
|
Prefix: "cookie",
|
||||||
Description: "单个cookie值",
|
Description: "单个cookie值",
|
||||||
IsRequest: true,
|
IsRequest: true,
|
||||||
|
HasParams: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "所有URL参数组合",
|
Name: "所有URL参数组合",
|
||||||
@@ -165,6 +169,7 @@ var AllCheckpoints = []*HTTPFirewallCheckpointDefinition{
|
|||||||
Prefix: "arg",
|
Prefix: "arg",
|
||||||
Description: "单个URL参数值",
|
Description: "单个URL参数值",
|
||||||
IsRequest: true,
|
IsRequest: true,
|
||||||
|
HasParams: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "所有Header信息",
|
Name: "所有Header信息",
|
||||||
@@ -177,11 +182,13 @@ var AllCheckpoints = []*HTTPFirewallCheckpointDefinition{
|
|||||||
Prefix: "header",
|
Prefix: "header",
|
||||||
Description: "单个Header值",
|
Description: "单个Header值",
|
||||||
IsRequest: true,
|
IsRequest: true,
|
||||||
|
HasParams: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "CC统计",
|
Name: "CC统计",
|
||||||
Prefix: "cc",
|
Prefix: "cc",
|
||||||
Description: "统计某段时间段内的请求信息",
|
Description: "统计某段时间段内的请求信息",
|
||||||
|
HasParams: true,
|
||||||
Params: []*KeyValue{
|
Params: []*KeyValue{
|
||||||
NewKeyValue("请求数", "requests"),
|
NewKeyValue("请求数", "requests"),
|
||||||
},
|
},
|
||||||
@@ -267,6 +274,7 @@ var AllCheckpoints = []*HTTPFirewallCheckpointDefinition{
|
|||||||
Prefix: "responseHeader",
|
Prefix: "responseHeader",
|
||||||
Description: "响应Header值",
|
Description: "响应Header值",
|
||||||
IsRequest: false,
|
IsRequest: false,
|
||||||
|
HasParams: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "响应内容",
|
Name: "响应内容",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ type HTTPFirewallRule struct {
|
|||||||
Id int64 `yaml:"id" json:"id"`
|
Id int64 `yaml:"id" json:"id"`
|
||||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||||
Param string `yaml:"param" json:"param"`
|
Param string `yaml:"param" json:"param"`
|
||||||
|
ParamFilters []*ParamFilter `yaml:"paramFilters" json:"paramFilters"`
|
||||||
Operator string `yaml:"operator" json:"operator"`
|
Operator string `yaml:"operator" json:"operator"`
|
||||||
Value string `yaml:"value" json:"value"`
|
Value string `yaml:"value" json:"value"`
|
||||||
IsCaseInsensitive bool `yaml:"isCaseInsensitive" json:"isCaseInsensitive"`
|
IsCaseInsensitive bool `yaml:"isCaseInsensitive" json:"isCaseInsensitive"`
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ const (
|
|||||||
HTTPFirewallRuleOperatorVersionLt HTTPFirewallRuleOperator = "version lt"
|
HTTPFirewallRuleOperatorVersionLt HTTPFirewallRuleOperator = "version lt"
|
||||||
HTTPFirewallRuleOperatorVersionRange HTTPFirewallRuleOperator = "version range"
|
HTTPFirewallRuleOperatorVersionRange HTTPFirewallRuleOperator = "version range"
|
||||||
|
|
||||||
|
HTTPFirewallRuleOperatorContainsBinary HTTPFirewallRuleOperator = "contains binary" // contains binary
|
||||||
|
HTTPFirewallRuleOperatorNotContainsBinary HTTPFirewallRuleOperator = "not contains binary" // not contains binary
|
||||||
|
|
||||||
// ip
|
// ip
|
||||||
HTTPFirewallRuleOperatorEqIP HTTPFirewallRuleOperator = "eq ip"
|
HTTPFirewallRuleOperatorEqIP HTTPFirewallRuleOperator = "eq ip"
|
||||||
HTTPFirewallRuleOperatorGtIP HTTPFirewallRuleOperator = "gt ip"
|
HTTPFirewallRuleOperatorGtIP HTTPFirewallRuleOperator = "gt ip"
|
||||||
@@ -132,6 +135,18 @@ var AllRuleOperators = []*RuleOperatorDefinition{
|
|||||||
Description: "包含某个后缀",
|
Description: "包含某个后缀",
|
||||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
|
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "包含二进制数据",
|
||||||
|
Code: HTTPFirewallRuleOperatorContainsBinary,
|
||||||
|
Description: "包含一组二进制数据",
|
||||||
|
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "不包含二进制数据",
|
||||||
|
Code: HTTPFirewallRuleOperatorNotContainsBinary,
|
||||||
|
Description: "不包含一组二进制数据",
|
||||||
|
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "包含索引",
|
Name: "包含索引",
|
||||||
Code: HTTPFirewallRuleOperatorHasKey,
|
Code: HTTPFirewallRuleOperatorHasKey,
|
||||||
|
|||||||
10
pkg/serverconfigs/firewallconfigs/param_filter.go
Normal file
10
pkg/serverconfigs/firewallconfigs/param_filter.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package firewallconfigs
|
||||||
|
|
||||||
|
import "github.com/iwind/TeaGo/maps"
|
||||||
|
|
||||||
|
// 对参数的过滤器
|
||||||
|
type ParamFilter struct {
|
||||||
|
Code string `yaml:"code" json:"code"` // 过滤器编号
|
||||||
|
Name string `yaml:"name" json:"name"` // 名称
|
||||||
|
Options maps.Map `yaml:"options" json:"options"` // 过滤器选项
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user