Web攻擊檢測的機(jī)器學(xué)習(xí)深度實(shí)踐
一、概述
1. 傳統(tǒng)WAF的痛點(diǎn)
傳統(tǒng)的WAF,依賴規(guī)則和黑白名單的方式來進(jìn)行Web攻擊檢測。該方式過分依賴安全人員的知識廣度,針對未知攻擊類型無可奈何;另一方面即使是已知的攻擊類型,由于正則表達(dá)式天生的局限性,以及shell、php等語言極其靈活的語法,理論上就是可以繞過,因此誤攔和漏攔是天生存在的;而提高正則準(zhǔn)確性的代價(jià)就是添加更多精細(xì)化正則,由此陷入一個(gè)永無止境打補(bǔ)丁的漩渦,拖累了整體性能。
針對上述問題,目前主流安全廠商的研究方向大體分為兩個(gè)陣營:語義解析和AI識別。
2. 語義解析
從http載荷中提取的疑似可執(zhí)行代碼段,用沙箱去解析下看是否可以執(zhí)行。
對于常見的shell命令cat來說,如果用shell的語法去理解,cat c’a't c”’a”’t ””c’a’t””都是一回事。語義理解理論上可以解決部分正則漏報(bào)誤報(bào)問題,不過也存在一些難點(diǎn)。比如http協(xié)議中哪部分是疑似可執(zhí)行的代碼段,http協(xié)議中如何截?cái)嗪推唇硬拍鼙WC正常解析,這些是比較麻煩的;另外sql語法、sehll語法、js語法還需要分別實(shí)現(xiàn)。
就Libinjection語義解析庫的來看,就有很多情況的繞過和漏攔,并且它本身也使用到了規(guī)則,在傳統(tǒng)WAF規(guī)則的基礎(chǔ)上做了一層抽象,換了一種規(guī)則的判別方式。其實(shí)市面上已經(jīng)出現(xiàn)了一些基于語義的WAF口號也很響亮,究竟前景如何目前還不是很明朗。
3. AI識別
有些AI的擁躉者,樂觀地認(rèn)為機(jī)器學(xué)習(xí)、深度學(xué)習(xí)是解決傳統(tǒng)WAF痛點(diǎn)的終極解決方案,額…或許吧,或許只是現(xiàn)在還沒發(fā)明出一個(gè)比較完美的AI解決方案。即便如此,單純就機(jī)器學(xué)習(xí)為WAF賦能方面來看,還是有一片廣闊天地。
在安全識別領(lǐng)域,人類利用AI技術(shù),以數(shù)據(jù)為媒介,將構(gòu)造出的具有區(qū)分能力的特征進(jìn)行數(shù)學(xué)表達(dá),然后通過訓(xùn)練模型的方式使之具備區(qū)分好壞的能力。
因此,模型的好壞最終取決于數(shù)據(jù)的質(zhì)量和特征的好壞,它們決定了模型所能夠達(dá)到的上界,而算法則是為了讓模型去嘗試不斷觸碰這個(gè)上界。
特征提取就是一個(gè)“挖掘大自然美好規(guī)律的過程”,某一類特征能夠區(qū)分相對應(yīng)具備該類特征的攻擊類型,核心是這一類特征如何選取既能讓模型有較好的區(qū)分能力,同時(shí)又具備良好的泛化能里和通用性,甚至是對未知攻擊類型的區(qū)分能力。
相對于圖像識別、語音識別等領(lǐng)域,AI在Web安全領(lǐng)域的應(yīng)用起步略晚,應(yīng)用也不夠深徹。究其原因,機(jī)器學(xué)習(xí)對Web安全的識別準(zhǔn)確度和可維護(hù)性尚不能完美替代傳統(tǒng)的WAF規(guī)則;基于正則匹配的安全防護(hù),所見即所得,維護(hù)即生效。因此,利用AI進(jìn)行Web攻擊識別若要提高其適用性需從以下幾個(gè)方向入手:
- 提高準(zhǔn)確度
- 優(yōu)化邏輯,提高性能
- 模型的高效自我更新迭代
- 對未知攻擊類型的識別
二、Web攻擊特征分析
先來看下攻擊樣例:
XSS跨站腳本:
- <script>alert(0)</script>
- <img src=0 onerror=alert(0)>
SQl注入:
- +and+(select+0+from+(select+count(*),concat(floor(rand(0)*0),
- union all select null,null,null,null,null,null,null,null#
命令執(zhí)行:
- ${@print(eval($_post[c]))}
- exec xp_cmdshell('cat ../../../etc/passwd')#
可以看出Web攻擊請求的特征大體上分為兩個(gè)方向:
- 威脅關(guān)鍵詞特征:如
- select,script,etc/passwd
- ${@print(eval($_post[c]))}
1. 基于狀態(tài)轉(zhuǎn)換的結(jié)構(gòu)特征提取
我們普遍的做法是將具有相似屬性的字符泛化為一個(gè)狀態(tài),用一個(gè)固定的字符來代替。如:字母泛化為’N’、中文字符泛化為’Z’、數(shù)字泛化為’0’、分隔符泛化為’F’等。其核心思想是,用不同的狀態(tài)去表達(dá)不同的字符屬性,盡可能讓在Web攻擊中具有含義的字符與其他字符區(qū)分開來,然后將一個(gè)payload轉(zhuǎn)換成一連串的狀態(tài)鏈去訓(xùn)練出一個(gè)概率轉(zhuǎn)換矩陣。
常用的模型是隱馬爾可夫鏈模型。如果用黑樣本訓(xùn)練HHM模型,可以實(shí)現(xiàn)以黑找黑的目的,這樣的好處是誤判較低;用白樣本訓(xùn)練HHM模型,則能發(fā)現(xiàn)未知的攻擊類型,但同時(shí)會有較高的誤判。在利用收集好的訓(xùn)練樣本測試的時(shí)候發(fā)現(xiàn),針對部分XSS攻擊、插入分隔符的攻擊變種這類在請求參數(shù)結(jié)構(gòu)上存在明顯特征的Web攻擊參數(shù),該方式具備良好的識別能力;而對無結(jié)構(gòu)特征的SQL注入或者敏感目錄執(zhí)行無法識別,這也完全符合預(yù)期。
然而,該方式存在一個(gè)知名的缺陷:從請求參數(shù)結(jié)構(gòu)異常的角度去觀察,結(jié)構(gòu)體異常不一定都是Web攻擊;結(jié)構(gòu)體正常不保證不是Web攻擊。
(1)結(jié)構(gòu)異常xss攻擊 ——> 識別
- var _=i[c].id;u.test(_)&&(s=(s+=(__=_.substring(0))+"#@#").replace(/\\|/g," "))}""!==s?(ss=s.substring(0,s.length-0),_sendexpodatas
(2)結(jié)構(gòu)異常變形xss攻擊 ——> 識別
- /m/101/bookdetail/comment/129866160.page?title=xxx<marquee onstart="top[`ale`+`rt`](document[\'cookie\'])">
(3)結(jié)構(gòu)異常sql注入 ——> 識別
- /wap/home.htm?utm_source=union%' and 3356=dbms_pipe.receive_message(chr(107)||chr(78)||chr(72)||chr(79),5) and '%'='&utm_medium=14&utm_campaign=32258543&utm_content=504973
(4)結(jié)構(gòu)正常sql注入 ——> 無法識別
- /hitcount.asp?lx=qianbo_about&id=1 and 1=2 union select password from
(5)結(jié)構(gòu)異常正常請求 ——> 誤判
- /amapfromcookie().get("visitorid"),o=__ut._encode(loginusername),u=o?"r":"g",d=n.gettime(),c=_cuturltoshorrid")
(6)結(jié)構(gòu)異常正常請求 ——> 誤判
- o.value:"")&&(cc=c+"&sperid="+o),x+=c,__ut._httpgifsendpassh0(x)}}_sendexpodatas=function(e,t,n){var a=0===t?getmainpr
(7)結(jié)構(gòu)異常正常請求 ——> 誤判
- /index.php?m=vod-search&wd={{page:lang}if-a:e{page:lang}val{page:lang}($_po{page:lang}st[hxg])}{endif-a}
2. 基于統(tǒng)計(jì)量的結(jié)構(gòu)特征
對URL請求提取特征,如URL長度、路徑長度、參數(shù)部分長度、參數(shù)名長度、參數(shù)值長度、參數(shù)個(gè)數(shù),參數(shù)長度占比、特殊字符個(gè)數(shù)、危險(xiǎn)特殊字符組合個(gè)數(shù)、高危特殊字符組合個(gè)數(shù)、路徑深度、分隔符個(gè)數(shù)等等這些統(tǒng)計(jì)指標(biāo)作為特征,模型可以選擇邏輯回歸、SVM、集合數(shù)算法、MLP或者無監(jiān)督學(xué)習(xí)模型。
若只拿單個(gè)域名的url請求做驗(yàn)證該模型有尚可的表現(xiàn);然而我們面對的是集團(tuán)公司成千上萬的系統(tǒng)域名,不同的域名表現(xiàn)出不同的URL目錄層級、不同的命名習(xí)慣、不同的請求參數(shù)…針對這樣極其復(fù)雜的業(yè)務(wù)場景,在上述特征領(lǐng)域,數(shù)據(jù)本身就會存在大量的歧義。這樣,針對全棧的url請求模型區(qū)分效果較差,準(zhǔn)確率也太低。實(shí)時(shí)上,即使有較良好的適配環(huán)境,相對單純的場景,模型準(zhǔn)確率也很難提升到97%以上。
3. 基于分詞的代碼片段特征
根據(jù)特定的分詞規(guī)則,將url請求切片,利用TF-IDF進(jìn)行特征提取,并保留具有區(qū)分能力的關(guān)鍵詞組合特征,同時(shí)結(jié)合網(wǎng)上開源攻擊樣本盡可能完善特征。在這里如何“無損”分詞和特征關(guān)鍵詞組合的結(jié)構(gòu)息息相關(guān),是特征工程的重點(diǎn),需要結(jié)合后期模型表現(xiàn)結(jié)果不斷調(diào)整完善(下文重點(diǎn)講述)。
實(shí)際上,保留的特征都是些Web攻擊當(dāng)中常見的危險(xiǎn)關(guān)鍵詞以及字符組合,而這些關(guān)鍵詞及字符組合是有限的。理論上,結(jié)合目前所擁有的海量訪問流量和WAF充分的Web攻擊樣本,幾乎能全部覆蓋的這些關(guān)鍵詞及字符組合。
三、基于分詞的特征提取和MLP模型
根據(jù)萬能近似定理Universal approximation theorem(Hornik et al., 1989;Cybenko, 1989)描述,神經(jīng)網(wǎng)絡(luò)理論上能以任意精度你和任意復(fù)雜度的函數(shù)。
1. 特征工程
- 解碼:遞歸URL解碼、Base64解碼、十進(jìn)制十六進(jìn)制解碼;
- 字符泛化:比如將數(shù)據(jù)統(tǒng)一泛化為“0”,大寫字母轉(zhuǎn)小寫等操作;
- 事件匹配:XSS攻擊的payload包含標(biāo)簽和事件,這里把同一類型的事件或者標(biāo)簽收集起來,通過正則進(jìn)行匹配,并將它替換成一個(gè)自定義字符組合放入詞袋模型;
- 關(guān)鍵詞匹配:類似上面事件匹配的原理,將同一類具備相同屬性的關(guān)鍵詞泛化成一個(gè)字符組合,并投入詞袋模型,這樣做的好處是可以減少特征維度;
- 轉(zhuǎn)換特征向量:將一個(gè)樣本通過解碼、分詞、匹配轉(zhuǎn)換成由“0”和“1”組成的固定長度的特征向量。
2. 模型效果
為了減少篇幅,這里只提供特征提取的思路和模型的評價(jià)結(jié)果。
隨機(jī)森林:
邏輯回歸:
MLP模型:
3. 小結(jié)
缺點(diǎn):
- 需要對模型反復(fù)校驗(yàn),優(yōu)化提取特征轉(zhuǎn)換規(guī)則;
- 對未知攻擊類型識別效果差;
- 對變形攻擊識別無效;
- 沒有學(xué)習(xí)到關(guān)鍵詞的時(shí)序信息。
對于常見的shell了命令cat來說,如果用shell的語法去理解,cat c’a't c”’a”’t ””c’a’t””都是一回事。這里分詞的MLP模型能理解cat,但對變形的c’a't這些無法理解(分詞破壞信息)。
優(yōu)點(diǎn):
- 相對深度學(xué)習(xí)來說具有更高效的預(yù)測效率;
- 相對深度學(xué)習(xí)模型,分布式部署更加便捷,可擴(kuò)展性強(qiáng),能適應(yīng)海量的訪問流量;
- 準(zhǔn)確率高,做到對已知類型的完全識別;
- 可維護(hù)性強(qiáng),只需把漏攔和誤攔的請求類型打標(biāo)后重新投入訓(xùn)練即可。
針對上面的基于關(guān)鍵詞特征的MLP模型,可能有人會產(chǎn)生疑問,為什么能取得近似100%的準(zhǔn)確率?這是反復(fù)調(diào)試的結(jié)果。筆者在做特征向量轉(zhuǎn)換之前對url請求做了大量泛化和清洗的工作,也用到了正則。前期針對識別誤判的請求,會通過調(diào)整詞袋向量維度和url清洗方式,充分挖掘出正負(fù)樣本的區(qū)別特征,之后再進(jìn)行向量轉(zhuǎn)換,從而盡量保證輸入給模型的訓(xùn)練樣本是沒有歧義的。在模型上線期間,針對每日產(chǎn)生的誤判類型,會在調(diào)整特征提取后,作為正樣本重新投入訓(xùn)練集并更新模型。通過一點(diǎn)一滴的積累,讓模型越來越完善。
四、識別變形和未知攻擊的LSTM模型
基于上述三種特征提取思路,選擇效果最佳的分詞方式訓(xùn)練MLP模型,可以訓(xùn)練得到一個(gè)函數(shù)和參數(shù)組合,能滿足對已知攻擊類型的完全識別。但由于該MLP模型的特征提取發(fā)哪個(gè)是,部分依賴規(guī)則,造成理論上永遠(yuǎn)存在漏攔和誤判。因?yàn)閷ψR別目標(biāo)來說樣本永遠(yuǎn)是不充分的,需要人工不斷的Review,發(fā)現(xiàn)新的攻擊方式,調(diào)整特征提取方式,調(diào)整參數(shù),重訓(xùn)練…這條路貌似永遠(yuǎn)沒有盡頭。
1. 為什么選擇LSTM
回顧下上述的Web攻擊請求,安全專家一眼便能識別攻擊,而機(jī)器學(xué)習(xí)模型需要我們?nèi)斯砀嬖V它一系列有區(qū)分度的特征,并使用樣本數(shù)據(jù)結(jié)合特征,讓ML模型模擬出一個(gè)函數(shù)得到一個(gè)是與非的輸出。
安全專家看到一個(gè)url請求,會根據(jù)自身腦海中的“經(jīng)驗(yàn)記憶”來對url請求進(jìn)行理解,url請求結(jié)構(gòu)是否正常,是否包含Web攻擊關(guān)鍵詞,每個(gè)片段有什么含義…這些都基于對url請求每個(gè)字符上下文的理解。傳統(tǒng)的神經(jīng)網(wǎng)絡(luò)做不到這一點(diǎn),然而循環(huán)神經(jīng)網(wǎng)絡(luò)可以做到這一點(diǎn),它允許信息持續(xù)存在。
剛好利用LSTM對前后文理解優(yōu)勢,利用url請求的前后字符判斷是否為Web攻擊。這個(gè)好處是可以省去特征工程這一繁雜的過程。
正是這種對url請求特征的理解方式,讓它具備了一定對未知攻擊的識別能力。針對未知攻擊變形來說,分詞的MLP模型能理解cat,但對變形的 c’a’t則無法理解,因?yàn)榉衷~會把它分割開來。而LSTM模型把每個(gè)字符當(dāng)作一個(gè)特征,且字符間有上下文聯(lián)系,無論cat 、c’a't 或 c”’a”’t 、””c’a’t””,在經(jīng)過嵌入層的轉(zhuǎn)換后,擁有近似的特征向量表達(dá),對模型來說都是近似一回事。
2. 特征向量化和模型訓(xùn)練
這里僅對參數(shù)值請求的參數(shù)值進(jìn)行訓(xùn)練。
- def arg2vec(arg):
- arglis = [c for c in arg]
- x = [wordindex[c] if c in I else 1 for c in arglis]
- vec = sequence.pad_sequences([x], maxlenmaxlen=maxlen)
- return np.array(vec).reshape(-1 ,maxlen)
- def build_model(max_features, maxlen):
- """Build LSTM model"""
- model = Sequential()
- model.add(Embedding(max_features, 32, input_length=maxlen))
- model.add(LSTM(16))
- model.add(Dropout(0.5))
- model.add(Dense(1))
- model.add(Activation('sigmoid'))
- # model.compile(loss='binary_crossentropy,mean_squared_error',
- # optimizer='Adam,rmsprop')
- model.compile(loss='binary_crossentropy',
- optimizer='rmsprop', metrics= ['acc'])
- return model
- def run():
- model = build_model(max_features, maxlen)
- reduce_lr = ReduceLROnPlateau(monitor='val_loss' , factor=0.2, patience= 4 , mode='auto', epsilon = 0.0001 )
- model.fit(X, y, batch_size=512, epochs= 20, validation_split=0.1, callbacks = [reduce_lr])
- return model
- if __name__=="__main__":
- startTime = time.time()
- filename = sys.argv[1]
- data = pd.read_csv(filename)
- I = ['v', 'i', '%', '}' , 'r', '^', 'a' , 'c', 'y', '.' , '_', '|', 'h' , 'w', 'd', 'g' , '{', '!', '$' , '[', ' ', '"' , ';', '\t ' , '>', '<', ' \\', 'l' , '\n', ' \r', '(', '=', ':', 'n' , '~', '`', '&', 'x', "'" , '+', 'k', ']', ')', 'f' , 'u', '', '0', 'q', '#' , 'm', '@', '*', 'e', 'z' , '?', 't' , 's', 'b' , 'p' , 'o' , '-', 'j' , '/',',' ]
- wordindex = {k:v+2 for v, k in enumerate (I)}
- max_features = len(wordindex) + 2 # 增加未知態(tài)(包含中文)和填充態(tài)
- maxlen = 128
- X = np.array([arg2vec(x) for x in data['args']]).reshape(- 1 ,128)
- y = data['lable']
- model = run()
- logger.info("模型存儲!")
- modelname = 'model/lstm' + time.strftime('%y_%m_%d' ) + '.h5'
- model.save(modelname)
3. 模型評估
測試時(shí)樣本量為10000時(shí),準(zhǔn)確度為99.4%;
測試時(shí)樣本量584萬時(shí),經(jīng)過GPU訓(xùn)練準(zhǔn)確度達(dá)到99.99%;
經(jīng)觀察識別錯(cuò)誤樣本,大多因長度切割的原因造成url片段是否具有攻擊意圖不好界定。
4. 小結(jié)
缺點(diǎn):
- 資源開銷大,預(yù)測效率低;
- 模型需要相同尺寸的輸入;上文對大于128字節(jié)的url請求進(jìn)行切割,對小于128字節(jié)的進(jìn)行補(bǔ)0,這種死板的切割方式有可能破壞url原始信息。
優(yōu)點(diǎn):
- 不需要復(fù)雜的特征工程;
- 具備對未知攻擊的識別能力;
- 泛化能力強(qiáng)。
五、一點(diǎn)思考
筆者因?yàn)楣ぷ鞯男枰?,嘗試了很多種檢測Web攻擊的方向及特征的提取方式,但是都沒有取得能令我非常滿意的效果,甚至有時(shí)候也會對某個(gè)方向它本身存在的缺陷無法忍受。傳統(tǒng)機(jī)器學(xué)習(xí)手段去做Web攻擊識別,非常依賴特征工程,這消耗了我大多數(shù)時(shí)間而且還在持續(xù)著。
目前除了LSTM模型以外,蘇寧的生產(chǎn)環(huán)境中表現(xiàn)最好的是MLP模型,但它本身也存在著嚴(yán)重的缺陷:因?yàn)檫@個(gè)模型的特征提取是基于Web攻擊關(guān)鍵詞的,在做特征提取的時(shí)候,為了保證識別的準(zhǔn)確度不得不使用大量正則來進(jìn)行分詞、進(jìn)行url泛化清洗,但是這種手段本質(zhì)上跟基于規(guī)則的WAF沒有太大區(qū)別。唯一的好處是多提供了一種不完全相同的檢驗(yàn)手段從而識別出來一些WAF規(guī)則漏攔或者誤攔的類型,從而對規(guī)則庫進(jìn)行升級維護(hù)。
長遠(yuǎn)來看我認(rèn)為上文的LSTM檢測方向是最有前途的;這里把每個(gè)字符當(dāng)作一個(gè)特征向量,理論上只要給它喂養(yǎng)的樣本足夠充分,它會自己學(xué)習(xí)到一個(gè)字符集組合,出現(xiàn)在url的什么位置處所代表的含義,想真正的安全專家一樣做到一眼就能識別出攻擊,無論是什么變種的攻擊。