mirror of
				https://github.com/TeaOSLab/EdgeNode.git
				synced 2025-11-04 16:00:25 +08:00 
			
		
		
		
	WAF操作符增加“包含XSS注入-严格模式”
This commit is contained in:
		@@ -58,7 +58,7 @@ int libinjection_sqli(const char* s, size_t slen, char fingerprint[]);
 | 
			
		||||
 * \return 1 if XSS found, 0 if benign
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
int libinjection_xss(const char* s, size_t slen);
 | 
			
		||||
int libinjection_xss(const char* s, size_t slen, int strictMode);
 | 
			
		||||
 | 
			
		||||
LIBINJECTION_END_DECLS
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ typedef enum attribute {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static attribute_t is_black_attr(const char* s, size_t len);
 | 
			
		||||
static int is_black_tag(const char* s, size_t len);
 | 
			
		||||
static int is_black_tag(const char* s, size_t len, int strictMode);
 | 
			
		||||
static int is_black_url(const char* s, size_t len);
 | 
			
		||||
static int cstrcasecmp_with_null(const char *a, const char *b, size_t n);
 | 
			
		||||
static int html_decode_char_at(const char* src, size_t len, size_t* consumed);
 | 
			
		||||
@@ -492,6 +492,35 @@ static stringtype_t BLACKATTR[] = {
 | 
			
		||||
  };
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// GoEdge: change BLACKTAG to STRICT_BLACKTAG
 | 
			
		||||
static const char* STRICT_BLACKTAG[] = {
 | 
			
		||||
    "APPLET"
 | 
			
		||||
    , "AUDIO"
 | 
			
		||||
    , "BASE"
 | 
			
		||||
    , "COMMENT"  /* IE http://html5sec.org/#38 */
 | 
			
		||||
    , "EMBED"
 | 
			
		||||
    , "FORM"
 | 
			
		||||
    , "FRAME"
 | 
			
		||||
    , "FRAMESET"
 | 
			
		||||
    , "HANDLER" /* Opera SVG, effectively a script tag */
 | 
			
		||||
    , "IFRAME"
 | 
			
		||||
    , "IMPORT"
 | 
			
		||||
    , "ISINDEX"
 | 
			
		||||
    , "LINK"
 | 
			
		||||
    , "LISTENER"
 | 
			
		||||
    /*    , "MARQUEE" */
 | 
			
		||||
    , "META"
 | 
			
		||||
    , "NOSCRIPT"
 | 
			
		||||
    , "OBJECT"
 | 
			
		||||
    , "SCRIPT"
 | 
			
		||||
    , "STYLE"
 | 
			
		||||
    , "VIDEO"
 | 
			
		||||
    , "VMLFRAME"
 | 
			
		||||
    , "XML"
 | 
			
		||||
    , "XSS"
 | 
			
		||||
    , NULL
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const char* BLACKTAG[] = {
 | 
			
		||||
    "APPLET"
 | 
			
		||||
    /*    , "AUDIO" */
 | 
			
		||||
@@ -515,7 +544,6 @@ static const char* BLACKTAG[] = {
 | 
			
		||||
    , "STYLE"
 | 
			
		||||
    /*    , "VIDEO" */
 | 
			
		||||
    , "VMLFRAME"
 | 
			
		||||
    , "XML"
 | 
			
		||||
    , "XSS"
 | 
			
		||||
    , NULL
 | 
			
		||||
};
 | 
			
		||||
@@ -606,7 +634,7 @@ static int htmlencode_startswith(const char *a, const char *b, size_t n)
 | 
			
		||||
    return (*a == 0) ? 1 : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int is_black_tag(const char* s, size_t len)
 | 
			
		||||
static int is_black_tag(const char* s, size_t len, int strictMode)
 | 
			
		||||
{
 | 
			
		||||
    const char** black;
 | 
			
		||||
 | 
			
		||||
@@ -614,7 +642,11 @@ static int is_black_tag(const char* s, size_t len)
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	if (strictMode == 1) {
 | 
			
		||||
    	black = STRICT_BLACKTAG;
 | 
			
		||||
    } else {
 | 
			
		||||
    	black = BLACKTAG;
 | 
			
		||||
    }
 | 
			
		||||
    while (*black != NULL) {
 | 
			
		||||
        if (cstrcasecmp_with_null(*black, s, len) == 0) {
 | 
			
		||||
            /* printf("Got black tag %s\n", *black); */
 | 
			
		||||
@@ -729,7 +761,7 @@ static int is_black_url(const char* s, size_t len)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int libinjection_is_xss(const char* s, size_t len, int flags)
 | 
			
		||||
int libinjection_is_xss(const char* s, size_t len, int flags, int strictMode)
 | 
			
		||||
{
 | 
			
		||||
    h5_state_t h5;
 | 
			
		||||
    attribute_t attr = TYPE_NONE;
 | 
			
		||||
@@ -743,7 +775,7 @@ int libinjection_is_xss(const char* s, size_t len, int flags)
 | 
			
		||||
        if (h5.token_type == DOCTYPE) {
 | 
			
		||||
            return 1;
 | 
			
		||||
        } else if (h5.token_type == TAG_NAME_OPEN) {
 | 
			
		||||
            if (is_black_tag(h5.token_start, h5.token_len)) {
 | 
			
		||||
            if (is_black_tag(h5.token_start, h5.token_len, strictMode)) {
 | 
			
		||||
                return 1;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (h5.token_type == ATTR_NAME) {
 | 
			
		||||
@@ -835,21 +867,21 @@ int libinjection_is_xss(const char* s, size_t len, int flags)
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
int libinjection_xss(const char* s, size_t slen)
 | 
			
		||||
int libinjection_xss(const char* s, size_t slen, int strictMode)
 | 
			
		||||
{
 | 
			
		||||
    if (libinjection_is_xss(s, slen, DATA_STATE)) {
 | 
			
		||||
    if (libinjection_is_xss(s, slen, DATA_STATE, strictMode)) {
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    if (libinjection_is_xss(s, slen, VALUE_NO_QUOTE)) {
 | 
			
		||||
    if (libinjection_is_xss(s, slen, VALUE_NO_QUOTE, strictMode)) {
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    if (libinjection_is_xss(s, slen, VALUE_SINGLE_QUOTE)) {
 | 
			
		||||
    if (libinjection_is_xss(s, slen, VALUE_SINGLE_QUOTE, strictMode)) {
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    if (libinjection_is_xss(s, slen, VALUE_DOUBLE_QUOTE)) {
 | 
			
		||||
    if (libinjection_is_xss(s, slen, VALUE_DOUBLE_QUOTE, strictMode)) {
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    if (libinjection_is_xss(s, slen, VALUE_BACK_QUOTE)) {
 | 
			
		||||
    if (libinjection_is_xss(s, slen, VALUE_BACK_QUOTE, strictMode)) {
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ extern "C" {
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
  int libinjection_is_xss(const char* s, size_t len, int flags);
 | 
			
		||||
  int libinjection_is_xss(const char* s, size_t len, int flags, int strictMode);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,4 +3,4 @@
 | 
			
		||||
#include "libinjection/src/libinjection_xss.c"
 | 
			
		||||
#include "libinjection/src/libinjection_html5.c"
 | 
			
		||||
 | 
			
		||||
#define GOEDGE_VERSION "23" // last version is for GoEdge change
 | 
			
		||||
#define GOEDGE_VERSION "24" // last version is for GoEdge change
 | 
			
		||||
@@ -19,7 +19,7 @@ import (
 | 
			
		||||
	"unsafe"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func DetectXSSCache(input string, cacheLife utils.CacheLife) bool {
 | 
			
		||||
func DetectXSSCache(input string, isStrict bool, cacheLife utils.CacheLife) bool {
 | 
			
		||||
	var l = len(input)
 | 
			
		||||
 | 
			
		||||
	if l == 0 {
 | 
			
		||||
@@ -27,17 +27,20 @@ func DetectXSSCache(input string, cacheLife utils.CacheLife) bool {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cacheLife <= 0 || l < 512 || l > utils.MaxCacheDataSize {
 | 
			
		||||
		return DetectXSS(input)
 | 
			
		||||
		return DetectXSS(input, isStrict)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var hash = xxhash.Sum64String(input)
 | 
			
		||||
	var key = "WAF@XSS@" + strconv.FormatUint(hash, 10)
 | 
			
		||||
	if isStrict {
 | 
			
		||||
		key += "@1"
 | 
			
		||||
	}
 | 
			
		||||
	var item = utils.SharedCache.Read(key)
 | 
			
		||||
	if item != nil {
 | 
			
		||||
		return item.Value == 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var result = DetectXSS(input)
 | 
			
		||||
	var result = DetectXSS(input, isStrict)
 | 
			
		||||
	if result {
 | 
			
		||||
		utils.SharedCache.Write(key, 1, fasttime.Now().Unix()+cacheLife)
 | 
			
		||||
	} else {
 | 
			
		||||
@@ -47,12 +50,12 @@ func DetectXSSCache(input string, cacheLife utils.CacheLife) bool {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DetectXSS detect XSS in string
 | 
			
		||||
func DetectXSS(input string) bool {
 | 
			
		||||
func DetectXSS(input string, isStrict bool) bool {
 | 
			
		||||
	if len(input) == 0 {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if detectXSSOne(input) {
 | 
			
		||||
	if detectXSSOne(input, isStrict) {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -63,22 +66,22 @@ func DetectXSS(input string) bool {
 | 
			
		||||
			var args = input[argsIndex+1:]
 | 
			
		||||
			unescapeArgs, err := url.QueryUnescape(args)
 | 
			
		||||
			if err == nil && args != unescapeArgs {
 | 
			
		||||
				return detectXSSOne(args) || detectXSSOne(unescapeArgs)
 | 
			
		||||
				return detectXSSOne(args, isStrict) || detectXSSOne(unescapeArgs, isStrict)
 | 
			
		||||
			} else {
 | 
			
		||||
				return detectXSSOne(args)
 | 
			
		||||
				return detectXSSOne(args, isStrict)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		unescapedInput, err := url.QueryUnescape(input)
 | 
			
		||||
		if err == nil && input != unescapedInput {
 | 
			
		||||
			return detectXSSOne(unescapedInput)
 | 
			
		||||
			return detectXSSOne(unescapedInput, isStrict)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func detectXSSOne(input string) bool {
 | 
			
		||||
func detectXSSOne(input string, isStrict bool) bool {
 | 
			
		||||
	if len(input) == 0 {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
@@ -86,5 +89,9 @@ func detectXSSOne(input string) bool {
 | 
			
		||||
	var cInput = C.CString(input)
 | 
			
		||||
	defer C.free(unsafe.Pointer(cInput))
 | 
			
		||||
 | 
			
		||||
	return C.libinjection_xss(cInput, C.size_t(len(input))) == 1
 | 
			
		||||
	var isStrictInt = 0
 | 
			
		||||
	if isStrict {
 | 
			
		||||
		isStrictInt = 1
 | 
			
		||||
	}
 | 
			
		||||
	return C.libinjection_xss(cInput, C.size_t(len(input)), C.int(isStrictInt)) == 1
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,18 +12,18 @@ import (
 | 
			
		||||
 | 
			
		||||
func TestDetectXSS(t *testing.T) {
 | 
			
		||||
	var a = assert.NewAssertion(t)
 | 
			
		||||
	a.IsFalse(injectionutils.DetectXSS(""))
 | 
			
		||||
	a.IsFalse(injectionutils.DetectXSS("abc"))
 | 
			
		||||
	a.IsTrue(injectionutils.DetectXSS("<script>"))
 | 
			
		||||
	a.IsTrue(injectionutils.DetectXSS("<link>"))
 | 
			
		||||
	a.IsFalse(injectionutils.DetectXSS("<html><span>"))
 | 
			
		||||
	a.IsFalse(injectionutils.DetectXSS("<script>"))
 | 
			
		||||
	a.IsTrue(injectionutils.DetectXSS("/path?onmousedown=a"))
 | 
			
		||||
	a.IsTrue(injectionutils.DetectXSS("/path?onkeyup=a"))
 | 
			
		||||
	a.IsTrue(injectionutils.DetectXSS("onkeyup=a"))
 | 
			
		||||
	a.IsTrue(injectionutils.DetectXSS("<iframe scrolling='no'>"))
 | 
			
		||||
	a.IsFalse(injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span></body></html>"))
 | 
			
		||||
	a.IsTrue(injectionutils.DetectXSS("name=s&description=%3Cscript+src%3D%22a.js%22%3Edddd%3C%2Fscript%3E"))
 | 
			
		||||
	a.IsFalse(injectionutils.DetectXSS("", true))
 | 
			
		||||
	a.IsFalse(injectionutils.DetectXSS("abc", true))
 | 
			
		||||
	a.IsTrue(injectionutils.DetectXSS("<script>", true))
 | 
			
		||||
	a.IsTrue(injectionutils.DetectXSS("<link>", true))
 | 
			
		||||
	a.IsFalse(injectionutils.DetectXSS("<html><span>", true))
 | 
			
		||||
	a.IsFalse(injectionutils.DetectXSS("<script>", true))
 | 
			
		||||
	a.IsTrue(injectionutils.DetectXSS("/path?onmousedown=a", true))
 | 
			
		||||
	a.IsTrue(injectionutils.DetectXSS("/path?onkeyup=a", true))
 | 
			
		||||
	a.IsTrue(injectionutils.DetectXSS("onkeyup=a", true))
 | 
			
		||||
	a.IsTrue(injectionutils.DetectXSS("<iframe scrolling='no'>", true))
 | 
			
		||||
	a.IsFalse(injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span></body></html>", true))
 | 
			
		||||
	a.IsTrue(injectionutils.DetectXSS("name=s&description=%3Cscript+src%3D%22a.js%22%3Edddd%3C%2Fscript%3E", true))
 | 
			
		||||
	a.IsFalse(injectionutils.DetectXSS(`<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 6.0.0">
 | 
			
		||||
   <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
 | 
			
		||||
      <rdf:Description rdf:about=""
 | 
			
		||||
@@ -31,11 +31,21 @@ func TestDetectXSS(t *testing.T) {
 | 
			
		||||
         <tiff:Orientation>1</tiff:Orientation>
 | 
			
		||||
      </rdf:Description>
 | 
			
		||||
   </rdf:RDF>
 | 
			
		||||
</x:xmpmeta>`)) // included in some photo files
 | 
			
		||||
</x:xmpmeta>`, true)) // included in some photo files
 | 
			
		||||
	a.IsFalse(injectionutils.DetectXSS(`<xml>
 | 
			
		||||
  
 | 
			
		||||
</xml>`, true))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestDetectXSS_Strict(t *testing.T) {
 | 
			
		||||
	var a = assert.NewAssertion(t)
 | 
			
		||||
	a.IsFalse(injectionutils.DetectXSS(`<xml>
 | 
			
		||||
  
 | 
			
		||||
</xml>`, false))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func BenchmarkDetectXSS_MISS(b *testing.B) {
 | 
			
		||||
	var result = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span></body></html>")
 | 
			
		||||
	var result = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span></body></html>", false)
 | 
			
		||||
	if result {
 | 
			
		||||
		b.Fatal("'result' should not be 'true'")
 | 
			
		||||
	}
 | 
			
		||||
@@ -44,13 +54,13 @@ func BenchmarkDetectXSS_MISS(b *testing.B) {
 | 
			
		||||
 | 
			
		||||
	b.RunParallel(func(pb *testing.PB) {
 | 
			
		||||
		for pb.Next() {
 | 
			
		||||
			_ = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span></body></html>")
 | 
			
		||||
			_ = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span></body></html>", false)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func BenchmarkDetectXSS_MISS_Cache(b *testing.B) {
 | 
			
		||||
	var result = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span></body></html>")
 | 
			
		||||
	var result = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span></body></html>", false)
 | 
			
		||||
	if result {
 | 
			
		||||
		b.Fatal("'result' should not be 'true'")
 | 
			
		||||
	}
 | 
			
		||||
@@ -59,13 +69,13 @@ func BenchmarkDetectXSS_MISS_Cache(b *testing.B) {
 | 
			
		||||
 | 
			
		||||
	b.RunParallel(func(pb *testing.PB) {
 | 
			
		||||
		for pb.Next() {
 | 
			
		||||
			_ = injectionutils.DetectXSSCache("<html><body><span>RequestId: 1234567890</span></body></html>", utils.CacheMiddleLife)
 | 
			
		||||
			_ = injectionutils.DetectXSSCache("<html><body><span>RequestId: 1234567890</span></body></html>", false, utils.CacheMiddleLife)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func BenchmarkDetectXSS_HIT(b *testing.B) {
 | 
			
		||||
	var result = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span><script src=\"\"></script></body></html>")
 | 
			
		||||
	var result = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span><script src=\"\"></script></body></html>", false)
 | 
			
		||||
	if !result {
 | 
			
		||||
		b.Fatal("'result' should not be 'false'")
 | 
			
		||||
	}
 | 
			
		||||
@@ -74,7 +84,7 @@ func BenchmarkDetectXSS_HIT(b *testing.B) {
 | 
			
		||||
 | 
			
		||||
	b.RunParallel(func(pb *testing.PB) {
 | 
			
		||||
		for pb.Next() {
 | 
			
		||||
			_ = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span><script src=\"\"></script></body></html>")
 | 
			
		||||
			_ = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span><script src=\"\"></script></body></html>", false)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -600,27 +600,28 @@ func (this *Rule) Test(value any) bool {
 | 
			
		||||
		default:
 | 
			
		||||
			return injectionutils.DetectSQLInjectionCache(this.stringifyValue(value), this.cacheLife)
 | 
			
		||||
		}
 | 
			
		||||
	case RuleOperatorContainsXSS:
 | 
			
		||||
	case RuleOperatorContainsXSS, RuleOperatorContainsXSSStrictly:
 | 
			
		||||
		if value == nil {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		var isStrict = this.Operator == RuleOperatorContainsXSSStrictly
 | 
			
		||||
		switch xValue := value.(type) {
 | 
			
		||||
		case []string:
 | 
			
		||||
			for _, v := range xValue {
 | 
			
		||||
				if injectionutils.DetectXSSCache(v, this.cacheLife) {
 | 
			
		||||
				if injectionutils.DetectXSSCache(v, isStrict, this.cacheLife) {
 | 
			
		||||
					return true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return false
 | 
			
		||||
		case [][]byte:
 | 
			
		||||
			for _, v := range xValue {
 | 
			
		||||
				if injectionutils.DetectXSSCache(string(v), this.cacheLife) {
 | 
			
		||||
				if injectionutils.DetectXSSCache(string(v), isStrict, this.cacheLife) {
 | 
			
		||||
					return true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return false
 | 
			
		||||
		default:
 | 
			
		||||
			return injectionutils.DetectXSSCache(this.stringifyValue(value), this.cacheLife)
 | 
			
		||||
			return injectionutils.DetectXSSCache(this.stringifyValue(value), isStrict, this.cacheLife)
 | 
			
		||||
		}
 | 
			
		||||
	case RuleOperatorContainsBinary:
 | 
			
		||||
		data, _ := base64.StdEncoding.DecodeString(this.stringifyValue(this.Value))
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@ const (
 | 
			
		||||
	RuleOperatorNotContainsAnyWord   RuleOperator = "not contains any word"
 | 
			
		||||
	RuleOperatorContainsSQLInjection RuleOperator = "contains sql injection"
 | 
			
		||||
	RuleOperatorContainsXSS          RuleOperator = "contains xss"
 | 
			
		||||
	RuleOperatorContainsXSSStrictly  RuleOperator = "contains xss strictly"
 | 
			
		||||
	RuleOperatorInIPList             RuleOperator = "in ip list"
 | 
			
		||||
	RuleOperatorHasKey               RuleOperator = "has key" // has key in slice or map
 | 
			
		||||
	RuleOperatorVersionGt            RuleOperator = "version gt"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user