WAF操作符增加“包含SQL注入-严格模式”

This commit is contained in:
GoEdgeLab
2024-01-16 20:41:52 +08:00
parent f5d4568695
commit c30b99025c
4 changed files with 78 additions and 61 deletions

View File

@@ -16,11 +16,12 @@ import (
"net/url" "net/url"
"strconv" "strconv"
"strings" "strings"
"unicode/utf8"
"unsafe" "unsafe"
) )
// DetectSQLInjectionCache detect sql injection in string with cache // DetectSQLInjectionCache detect sql injection in string with cache
func DetectSQLInjectionCache(input string, cacheLife utils.CacheLife) bool { func DetectSQLInjectionCache(input string, isStrict bool, cacheLife utils.CacheLife) bool {
var l = len(input) var l = len(input)
if l == 0 { if l == 0 {
@@ -28,7 +29,7 @@ func DetectSQLInjectionCache(input string, cacheLife utils.CacheLife) bool {
} }
if cacheLife <= 0 || l < 128 || l > utils.MaxCacheDataSize { if cacheLife <= 0 || l < 128 || l > utils.MaxCacheDataSize {
return DetectSQLInjection(input) return DetectSQLInjection(input, isStrict)
} }
var hash = xxhash.Sum64String(input) var hash = xxhash.Sum64String(input)
@@ -38,7 +39,7 @@ func DetectSQLInjectionCache(input string, cacheLife utils.CacheLife) bool {
return item.Value == 1 return item.Value == 1
} }
var result = DetectSQLInjection(input) var result = DetectSQLInjection(input, isStrict)
if result { if result {
utils.SharedCache.Write(key, 1, fasttime.Now().Unix()+cacheLife) utils.SharedCache.Write(key, 1, fasttime.Now().Unix()+cacheLife)
} else { } else {
@@ -48,11 +49,23 @@ func DetectSQLInjectionCache(input string, cacheLife utils.CacheLife) bool {
} }
// DetectSQLInjection detect sql injection in string // DetectSQLInjection detect sql injection in string
func DetectSQLInjection(input string) bool { func DetectSQLInjection(input string, isStrict bool) bool {
if len(input) == 0 { if len(input) == 0 {
return false return false
} }
if !isStrict {
if len(input) > 1024 {
if !utf8.ValidString(input[:1024]) && !utf8.ValidString(input[:1023]) && !utf8.ValidString(input[:1022]) {
return false
}
} else {
if !utf8.ValidString(input) {
return false
}
}
}
if detectSQLInjectionOne(input) { if detectSQLInjectionOne(input) {
return true return true
} }

View File

@@ -15,21 +15,23 @@ import (
func TestDetectSQLInjection(t *testing.T) { func TestDetectSQLInjection(t *testing.T) {
var a = assert.NewAssertion(t) var a = assert.NewAssertion(t)
a.IsTrue(injectionutils.DetectSQLInjection("' UNION SELECT * FROM myTable")) for _, isStrict := range []bool{true, false} {
a.IsTrue(injectionutils.DetectSQLInjection("id=1 ' UNION select * from a")) a.IsTrue(injectionutils.DetectSQLInjection("' UNION SELECT * FROM myTable", isStrict))
a.IsTrue(injectionutils.DetectSQLInjection("asdf asd ; -1' and 1=1 union/* foo */select load_file('/etc/passwd')--")) a.IsTrue(injectionutils.DetectSQLInjection("id=1 ' UNION select * from a", isStrict))
a.IsFalse(injectionutils.DetectSQLInjection("' UNION SELECT1 * FROM myTable")) a.IsTrue(injectionutils.DetectSQLInjection("asdf asd ; -1' and 1=1 union/* foo */select load_file('/etc/passwd')--", isStrict))
a.IsFalse(injectionutils.DetectSQLInjection("1234")) a.IsFalse(injectionutils.DetectSQLInjection("' UNION SELECT1 * FROM myTable", isStrict))
a.IsFalse(injectionutils.DetectSQLInjection("")) a.IsFalse(injectionutils.DetectSQLInjection("1234", isStrict))
a.IsTrue(injectionutils.DetectSQLInjection("id=123 OR 1=1&b=2")) a.IsFalse(injectionutils.DetectSQLInjection("", isStrict))
a.IsTrue(injectionutils.DetectSQLInjection("id=123&b=456&c=1' or 2=2")) a.IsTrue(injectionutils.DetectSQLInjection("id=123 OR 1=1&b=2", isStrict))
a.IsFalse(injectionutils.DetectSQLInjection("?")) a.IsTrue(injectionutils.DetectSQLInjection("id=123&b=456&c=1' or 2=2", isStrict))
a.IsFalse(injectionutils.DetectSQLInjection("/hello?age=22")) a.IsFalse(injectionutils.DetectSQLInjection("?", isStrict))
a.IsTrue(injectionutils.DetectSQLInjection("/sql/injection?id=123 or 1=1")) a.IsFalse(injectionutils.DetectSQLInjection("/hello?age=22", isStrict))
a.IsTrue(injectionutils.DetectSQLInjection("/sql/injection?id=123%20or%201=1")) a.IsTrue(injectionutils.DetectSQLInjection("/sql/injection?id=123 or 1=1", isStrict))
a.IsTrue(injectionutils.DetectSQLInjection("https://example.com/sql/injection?id=123%20or%201=1")) a.IsTrue(injectionutils.DetectSQLInjection("/sql/injection?id=123%20or%201=1", isStrict))
a.IsTrue(injectionutils.DetectSQLInjection("id=123%20or%201=1")) a.IsTrue(injectionutils.DetectSQLInjection("https://example.com/sql/injection?id=123%20or%201=1", isStrict))
a.IsTrue(injectionutils.DetectSQLInjection("https://example.com/' or 1=1")) a.IsTrue(injectionutils.DetectSQLInjection("id=123%20or%201=1", isStrict))
a.IsTrue(injectionutils.DetectSQLInjection("https://example.com/' or 1=1", isStrict))
}
} }
func BenchmarkDetectSQLInjection(b *testing.B) { func BenchmarkDetectSQLInjection(b *testing.B) {
@@ -37,7 +39,7 @@ func BenchmarkDetectSQLInjection(b *testing.B) {
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
_ = injectionutils.DetectSQLInjection("asdf asd ; -1' and 1=1 union/* foo */select load_file('/etc/passwd')--") _ = injectionutils.DetectSQLInjection("asdf asd ; -1' and 1=1 union/* foo */select load_file('/etc/passwd')--", false)
} }
}) })
} }
@@ -47,7 +49,7 @@ func BenchmarkDetectSQLInjection_URL(b *testing.B) {
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
_ = injectionutils.DetectSQLInjection("/sql/injection?id=123 or 1=1") _ = injectionutils.DetectSQLInjection("/sql/injection?id=123 or 1=1", false)
} }
}) })
} }
@@ -57,7 +59,7 @@ func BenchmarkDetectSQLInjection_Normal_Small(b *testing.B) {
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
_ = injectionutils.DetectSQLInjection("a/sql/injection?id=1234") _ = injectionutils.DetectSQLInjection("a/sql/injection?id=1234", false)
} }
}) })
} }
@@ -67,7 +69,7 @@ func BenchmarkDetectSQLInjection_URL_Normal_Small(b *testing.B) {
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
_ = injectionutils.DetectSQLInjection("/sql/injection?id=" + types.String(rands.Int64()%10000)) _ = injectionutils.DetectSQLInjection("/sql/injection?id="+types.String(rands.Int64()%10000), false)
} }
}) })
} }
@@ -77,7 +79,7 @@ func BenchmarkDetectSQLInjection_URL_Normal_Middle(b *testing.B) {
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
_ = injectionutils.DetectSQLInjection("/search?q=libinjection+fingerprint&newwindow=1&sca_esv=589290862&sxsrf=AMwHvKnxuLoejn2XlNniffC12E_xc35M7Q%3A1702090118361&ei=htvzzebfFZfo1e8PvLGggAk&ved=0ahUKEwjTsYmnq4GDAxUWdPOHHbwkCJAQ4ddDCBA&uact=5&oq=libinjection+fingerprint&gs_lp=Egxnd3Mtd2l6LXNlcnAiGIxpYmluamVjdGlvbmBmaW5nKXJwcmludTIEEAAYHjIGVAAYCBgeSiEaUPkRWKFZcAJ4AZABAJgBHgGgAfoEqgwDMC40uAEGyAEA-AEBwgIKEAFYTxjWMuiwA-IDBBgAVteIBgGQBgI&sclient=gws-wiz-serp#ip=1") _ = injectionutils.DetectSQLInjection("/search?q=libinjection+fingerprint&newwindow=1&sca_esv=589290862&sxsrf=AMwHvKnxuLoejn2XlNniffC12E_xc35M7Q%3A1702090118361&ei=htvzzebfFZfo1e8PvLGggAk&ved=0ahUKEwjTsYmnq4GDAxUWdPOHHbwkCJAQ4ddDCBA&uact=5&oq=libinjection+fingerprint&gs_lp=Egxnd3Mtd2l6LXNlcnAiGIxpYmluamVjdGlvbmBmaW5nKXJwcmludTIEEAAYHjIGVAAYCBgeSiEaUPkRWKFZcAJ4AZABAJgBHgGgAfoEqgwDMC40uAEGyAEA-AEBwgIKEAFYTxjWMuiwA-IDBBgAVteIBgGQBgI&sclient=gws-wiz-serp#ip=1", false)
} }
}) })
} }
@@ -87,7 +89,7 @@ func BenchmarkDetectSQLInjection_URL_Normal_Small_Cache(b *testing.B) {
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
_ = injectionutils.DetectSQLInjectionCache("/sql/injection?id="+types.String(rands.Int64()%10000), utils.CacheMiddleLife) _ = injectionutils.DetectSQLInjectionCache("/sql/injection?id="+types.String(rands.Int64()%10000), false, utils.CacheMiddleLife)
} }
}) })
} }
@@ -100,7 +102,7 @@ func BenchmarkDetectSQLInjection_Normal_Large(b *testing.B) {
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
_ = injectionutils.DetectSQLInjection("a/sql/injection?id=" + types.String(rands.Int64()%10000) + "&s=" + s + "&v=%20") _ = injectionutils.DetectSQLInjection("a/sql/injection?id="+types.String(rands.Int64()%10000)+"&s="+s+"&v=%20", false)
} }
}) })
} }
@@ -112,7 +114,7 @@ func BenchmarkDetectSQLInjection_Normal_Large_Cache(b *testing.B) {
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
_ = injectionutils.DetectSQLInjectionCache("a/sql/injection?id="+types.String(rands.Int64()%10000)+"&s="+s, utils.CacheMiddleLife) _ = injectionutils.DetectSQLInjectionCache("a/sql/injection?id="+types.String(rands.Int64()%10000)+"&s="+s, false, utils.CacheMiddleLife)
} }
}) })
} }
@@ -122,7 +124,7 @@ func BenchmarkDetectSQLInjection_URL_Unescape(b *testing.B) {
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
_ = injectionutils.DetectSQLInjection("/sql/injection?id=123%20or%201=1") _ = injectionutils.DetectSQLInjection("/sql/injection?id=123%20or%201=1", false)
} }
}) })
} }

View File

@@ -578,27 +578,28 @@ func (this *Rule) Test(value any) bool {
return runes.ContainsAllWords(this.stringifyValue(value), this.stringValues, this.IsCaseInsensitive) return runes.ContainsAllWords(this.stringifyValue(value), this.stringValues, this.IsCaseInsensitive)
case RuleOperatorNotContainsAnyWord: case RuleOperatorNotContainsAnyWord:
return !runes.ContainsAnyWordRunes(this.stringifyValue(value), this.stringValueRunes, this.IsCaseInsensitive) return !runes.ContainsAnyWordRunes(this.stringifyValue(value), this.stringValueRunes, this.IsCaseInsensitive)
case RuleOperatorContainsSQLInjection: case RuleOperatorContainsSQLInjection, RuleOperatorContainsSQLInjectionStrictly:
if value == nil { if value == nil {
return false return false
} }
var isStrict = this.Operator == RuleOperatorContainsSQLInjectionStrictly
switch xValue := value.(type) { switch xValue := value.(type) {
case []string: case []string:
for _, v := range xValue { for _, v := range xValue {
if injectionutils.DetectSQLInjectionCache(v, this.cacheLife) { if injectionutils.DetectSQLInjectionCache(v, isStrict, this.cacheLife) {
return true return true
} }
} }
return false return false
case [][]byte: case [][]byte:
for _, v := range xValue { for _, v := range xValue {
if injectionutils.DetectSQLInjectionCache(string(v), this.cacheLife) { if injectionutils.DetectSQLInjectionCache(string(v), isStrict, this.cacheLife) {
return true return true
} }
} }
return false return false
default: default:
return injectionutils.DetectSQLInjectionCache(this.stringifyValue(value), this.cacheLife) return injectionutils.DetectSQLInjectionCache(this.stringifyValue(value), isStrict, this.cacheLife)
} }
case RuleOperatorContainsXSS, RuleOperatorContainsXSSStrictly: case RuleOperatorContainsXSS, RuleOperatorContainsXSSStrictly:
if value == nil { if value == nil {

View File

@@ -4,35 +4,36 @@ type RuleOperator = string
type RuleCaseInsensitive = string type RuleCaseInsensitive = string
const ( const (
RuleOperatorGt RuleOperator = "gt" RuleOperatorGt RuleOperator = "gt"
RuleOperatorGte RuleOperator = "gte" RuleOperatorGte RuleOperator = "gte"
RuleOperatorLt RuleOperator = "lt" RuleOperatorLt RuleOperator = "lt"
RuleOperatorLte RuleOperator = "lte" RuleOperatorLte RuleOperator = "lte"
RuleOperatorEq RuleOperator = "eq" RuleOperatorEq RuleOperator = "eq"
RuleOperatorNeq RuleOperator = "neq" RuleOperatorNeq RuleOperator = "neq"
RuleOperatorEqString RuleOperator = "eq string" RuleOperatorEqString RuleOperator = "eq string"
RuleOperatorNeqString RuleOperator = "neq string" RuleOperatorNeqString RuleOperator = "neq string"
RuleOperatorMatch RuleOperator = "match" RuleOperatorMatch RuleOperator = "match"
RuleOperatorNotMatch RuleOperator = "not match" RuleOperatorNotMatch RuleOperator = "not match"
RuleOperatorWildcardMatch RuleOperator = "wildcard match" RuleOperatorWildcardMatch RuleOperator = "wildcard match"
RuleOperatorWildcardNotMatch RuleOperator = "wildcard not match" RuleOperatorWildcardNotMatch RuleOperator = "wildcard not match"
RuleOperatorContains RuleOperator = "contains" RuleOperatorContains RuleOperator = "contains"
RuleOperatorNotContains RuleOperator = "not contains" RuleOperatorNotContains RuleOperator = "not contains"
RuleOperatorPrefix RuleOperator = "prefix" RuleOperatorPrefix RuleOperator = "prefix"
RuleOperatorSuffix RuleOperator = "suffix" RuleOperatorSuffix RuleOperator = "suffix"
RuleOperatorContainsAny RuleOperator = "contains any" RuleOperatorContainsAny RuleOperator = "contains any"
RuleOperatorContainsAll RuleOperator = "contains all" RuleOperatorContainsAll RuleOperator = "contains all"
RuleOperatorContainsAnyWord RuleOperator = "contains any word" RuleOperatorContainsAnyWord RuleOperator = "contains any word"
RuleOperatorContainsAllWords RuleOperator = "contains all words" RuleOperatorContainsAllWords RuleOperator = "contains all words"
RuleOperatorNotContainsAnyWord RuleOperator = "not contains any word" RuleOperatorNotContainsAnyWord RuleOperator = "not contains any word"
RuleOperatorContainsSQLInjection RuleOperator = "contains sql injection" RuleOperatorContainsSQLInjection RuleOperator = "contains sql injection"
RuleOperatorContainsXSS RuleOperator = "contains xss" RuleOperatorContainsSQLInjectionStrictly RuleOperator = "contains sql injection strictly"
RuleOperatorContainsXSSStrictly RuleOperator = "contains xss strictly" RuleOperatorContainsXSS RuleOperator = "contains xss"
RuleOperatorInIPList RuleOperator = "in ip list" RuleOperatorContainsXSSStrictly RuleOperator = "contains xss strictly"
RuleOperatorHasKey RuleOperator = "has key" // has key in slice or map RuleOperatorInIPList RuleOperator = "in ip list"
RuleOperatorVersionGt RuleOperator = "version gt" RuleOperatorHasKey RuleOperator = "has key" // has key in slice or map
RuleOperatorVersionLt RuleOperator = "version lt" RuleOperatorVersionGt RuleOperator = "version gt"
RuleOperatorVersionRange RuleOperator = "version range" RuleOperatorVersionLt RuleOperator = "version lt"
RuleOperatorVersionRange RuleOperator = "version range"
RuleOperatorContainsBinary RuleOperator = "contains binary" // contains binary RuleOperatorContainsBinary RuleOperator = "contains binary" // contains binary
RuleOperatorNotContainsBinary RuleOperator = "not contains binary" // not contains binary RuleOperatorNotContainsBinary RuleOperator = "not contains binary" // not contains binary