如何使用深度學(xué)習(xí)檢測(cè)XSS
一、前言
眾所周知,深度學(xué)習(xí)在計(jì)算機(jī)視覺(jué)、自然語(yǔ)言處理、人工智能等領(lǐng)域取得了極大的進(jìn)展,在安全領(lǐng)域也開(kāi)始嶄露頭角走向了實(shí)際應(yīng)用。本文中進(jìn)行的實(shí)驗(yàn)主要以文本分類的方法,使用深度學(xué)習(xí)檢測(cè)XSS攻擊,由于本人是初學(xué)者,難免對(duì)算法本身的理解不夠確切,所以本文盡量使用通俗簡(jiǎn)單的方式介紹算法,不會(huì)過(guò)多的講解細(xì)節(jié),以免誤導(dǎo)大家。
二、數(shù)據(jù)集
安全領(lǐng)域的公開(kāi)數(shù)據(jù)集非常的稀缺,本文提供的實(shí)驗(yàn)數(shù)據(jù)包含兩個(gè)部分:從xssed爬取的黑樣本作為正樣例,有4萬(wàn)多條;另外提供約20萬(wàn)條正常的http get請(qǐng)求記錄作為負(fù)樣例,為了保證數(shù)據(jù)安全,去除了url中的host、path等信息,僅保留了payload的部分。
以上數(shù)據(jù)url編碼后保存在csv中,由于部分原始數(shù)據(jù)進(jìn)行過(guò)url編碼,所以要兩次url解碼后才能使用。
正樣例:
- topic=http://gmwgroup.harvard.edu/techniques/index.php?topic=<script>alert(document.cookie)</script>
- siteID=';alert(String.fromCharCode(88,83,83))//\';alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//\";alert(String.fromCharCode(88,83,83))//--></SCRIPT>">'><SCRIPT>alert(String.fromCharCode(88,83,83))</SCRIPT>
- js='"--></style></script><script>alert(/meehinfected/)</script></title><marquee><h1>XSS:)</h1><marquee><strong><blink>XSSTEST</blink></strong></marquee><h1 >XSS :) </h1></marquee>
負(fù)樣例:
- _=1498584888937/&list=FU1804,FU0,FU1707,FU1708,FU1709,FU1710,FU1711,FU1712
- hid=sgpy-windows-generic-device-id&v=8.4.0.1062&brand=1&platform=6&ifbak=0&ifmobile=0&ifauto=1&type=1&filename=sgim_privilege.zip
- iid=11491672248&device_id=34942737887&ac=wifi&channel=huawei&aid=13&app_name=news_article&version_code=621&version_name=6.2.1&device_platform=android&ssmix=a&device_type=FDR-A03L&device_brand=HUAWEI&language=zh&os_api=22&os_version=5.1.1&uuid=860947033207318&openudid=fc19d05187ebeb0&manifest_version_code=621&resolution=1200*1848&dpi=240&update_version_code=6214&_rticket=1498580286466
三、分詞
使用文本分類的方法自然涉及到如何將文本分詞。觀察以上正樣例,人是如何辨別XSS的:參數(shù)內(nèi)包含完整可執(zhí)行的HTML標(biāo)簽和DOM方法。所以要支持的分詞原則為:
單雙引號(hào)包含的內(nèi)容 ‘xss’
http/https鏈接
<>標(biāo)簽 <script>
<>開(kāi)頭 <h1
參數(shù)名 topic=
函數(shù)體 alert(
字符數(shù)字組成的單詞
另外,為了減小分詞數(shù)量,需要把數(shù)字和超鏈接范化,將數(shù)字替換為”0”,超鏈接替換為http://u。
實(shí)現(xiàn)的代碼如下:
- def GeneSeg(payload):
- payload=payload.lower()
- payload=unquote(unquote(payload))
- payload,num=re.subn(r'\d+',"0",payload)
- payload,num=re.subn(r'(http|https)://[a-zA-Z0-9\.@&/#!#\?]+',"http://u", payload)
- r = '''
- (?x)[\w\.]+?\(
- |\)
- |"\w+?"
- |'\w+?'
- |http://\w
- |</\w+>
- |<\w+>
- |<\w+
- |\w+=
- |>
- |[\w\.]+
- '''
- return nltk.regexp_tokenize(payload,r)
分詞后的正樣例:
- ['topic=', 'http://u', '<script>', 'alert(','document.cookie', ')', '</script>']
- ['siteid=', 'alert(', 'string.fromcharcode(', '0','0', '0', ')', ')', 'alert(', 'string.fromcharcode(', '0', '0', '0', ')', ')','alert(', 'string.fromcharcode(', '0', '0', '0', ')', ')', 'alert(','string.fromcharcode(', '0', '0', '0', ')', ')', '>', '</script>','>', '>', '<script>', 'alert(', 'string.fromcharcode(', '0', '0','0', ')', ')', '</script>']
- ['js=', '>', '</style>', '</script>','<script>', 'alert(', 'meeh', 'infected', ')', '</script>','</title>', '<marquee>', '<h0>', 'xss', ')', '</h0>','<marquee>', '<strong>', '<blink>', 'xss', 'test','</blink>', '</strong>', '</marquee>', '<h0', '>','xss', ')', '</h0>', '</marquee>']
分詞后的負(fù)樣例:
- ['_=', '0', 'list=', 'fu0', 'fu0', 'fu0', 'fu0','fu0', 'fu0', 'fu0', 'fu0']
- ['hid=', 'sgpy', 'windows', 'generic', 'device', 'id','v=', '0.0.0.0', 'brand=', '0', 'platform=', '0', 'ifbak=', '0', 'ifmobile=','0', 'ifauto=', '0', 'type=', '0', 'filename=', 'sgim_privilege.zip']
- ['iid=', '0', 'device_id=', '0', 'ac=', 'wifi','channel=', 'huawei', 'aid=', '0', 'app_name=', 'news_article','version_code=', '0', 'version_name=', '0.0.0', 'device_platform=', 'android','ssmix=', 'a', 'device_type=', 'fdr', 'a0l', 'device_brand=', 'huawei','language=', 'zh', 'os_api=', '0', 'os_version=', '0.0.0', 'uuid=', '0','openudid=', 'fc0d0ebeb0', 'manifest_version_code=', '0', 'resolution=', '0','0', 'dpi=', '0', 'update_version_code=', '0', '_rticket=', '0']
四、嵌入式詞向量
如何將分詞后的文本轉(zhuǎn)化為機(jī)器學(xué)習(xí)的問(wèn)題,第一步是要找到一種方法把這些詞數(shù)學(xué)化。最常見(jiàn)的方法是獨(dú)熱編碼(one-hot),這種方法是把詞表表示為一個(gè)很長(zhǎng)的向量,只有一個(gè)維度的值為1,其他都為0,如””””<script>”表示為[0,0,0,1,0,0,0,0…….]。這種方法存在一個(gè)重要的問(wèn)題是,構(gòu)成文本的向量是極其稀疏的,詞與詞之間是相互獨(dú)立的,機(jī)器學(xué)習(xí)無(wú)法理解詞的語(yǔ)義。嵌入式詞向量就是通過(guò)學(xué)習(xí)文本來(lái)用詞向量表征詞的語(yǔ)義信息,通過(guò)將詞嵌入空間使得語(yǔ)義相似的詞在空間內(nèi)的距離接近。空間向量可以表達(dá)如“話筒”和“麥克”這樣的同義詞,”cat”、”dog”、”fish”等詞在空間中也會(huì)聚集到一起。
在這里我們要使用嵌入式詞向量模型建立一個(gè)XSS的語(yǔ)義模型,讓機(jī)器能夠理解<script>、alert()這樣的HTML語(yǔ)言。取正樣例中出現(xiàn)次數(shù)最多的3000個(gè)詞,構(gòu)成詞匯表,其他的詞標(biāo)記為“UKN”,使用gensim模塊的word2vec類建模,詞空間維度取128維。
核心代碼:
- def build_dataset(datas,words):
- count=[["UNK",-1]]
- counter=Counter(words)
- count.extend(counter.most_common(vocabulary_size-1))
- vocabulary=[c[0] for c in count]
- data_set=[]
- for data in datas:
- d_set=[]
- for word in data:
- if word in vocabulary:
- d_set.append(word)
- else:
- d_set.append("UNK")
- count[0][1]+=1
- data_set.append(d_set)
- return data_set
- data_set=build_dataset(datas,words)
- model=Word2Vec(data_set,size=embedding_size,window=skip_window,negative=num_sampled,iter=num_iter)
- embeddings=model.wv
五、數(shù)據(jù)預(yù)處理
通過(guò)建立好的詞向量模型,我們就可以用空間向量表示一個(gè)文本,結(jié)合前面的過(guò)程,完整的流程如圖:
最后將全部數(shù)據(jù)隨機(jī)切分為70%訓(xùn)練數(shù)據(jù)和30%測(cè)試數(shù)據(jù),用于以下三個(gè)神經(jīng)網(wǎng)絡(luò)的訓(xùn)練和測(cè)試,代碼示例:
- from sklearn.model_selection import train_test_split
- train_datas,test_datas,train_labels,test_labels=train_test_split(datas,labels,test_size=0.3)
六、多層感知機(jī)
多層感知機(jī)(MLP)包含一個(gè)輸入層、輸出層和若干隱藏層。Keras可以使用Tensorflow作為后端輕松實(shí)現(xiàn)多層感知機(jī),最終整個(gè)算法的準(zhǔn)確率為99.9%,召回率為97.5%。核心代碼如下:
模型訓(xùn)練:
- deftrain(train_generator,train_size,input_num,dims_num):
- print("Start Train Job! ")
- start=time.time()
- inputs=InputLayer(input_shape=(input_num,dims_num),batch_size=batch_size)
- layer1=Dense(100,activation="relu")
- layer2=Dense(20,activation="relu")
- flatten=Flatten()
- layer3=Dense(2,activation="softmax",name="Output")
- optimizer=Adam()
- model=Sequential()
- model.add(inputs)
- model.add(layer1)
- model.add(Dropout(0.5))
- model.add(layer2)
- model.add(Dropout(0.5))
- model.add(flatten)
- model.add(layer3)
- call=TensorBoard(log_dir=log_dir,write_grads=True,histogram_freq=1)
- model.compile(optimizer,loss="categorical_crossentropy",metrics=["accuracy"])
- model.fit_generator(train_generator,steps_per_epoch=train_size//batch_size,epochs=epochs_num,callbacks=[call])
測(cè)試:
- deftest(model_dir,test_generator,test_size,input_num,dims_num,batch_size):
- model=load_model(model_dir)
- labels_pre=[]
- labels_true=[]
- batch_num=test_size//batch_size+1
- steps=0
- for batch,labels in test_generator:
- if len(labels)==batch_size:
- labels_pre.extend(model.predict_on_batch(batch))
- else:
- batch=np.concatenate((batch,np.zeros((batch_size-len(labels),input_num,dims_num))))
- labels_pre.extend(model.predict_on_batch(batch)[0:len(labels)])
- labels_true.extend(labels)
- steps+=1
- print("%d/%dbatch"%(steps,batch_num))
- labels_pre=np.array(labels_pre).round()
- def to_y(labels):
- y=[]
- for i in range(len(labels)):
- if labels[i][0]==1:
- y.append(0)
- else:
- y.append(1)
- return y
- y_true=to_y(labels_true)
- y_pre=to_y(labels_pre)
- precision=precision_score(y_true,y_pre)
- recall=recall_score(y_true,y_pre)
- print("Precision score is:",precision)
- print("Recall score is:",recall)
七、循環(huán)神經(jīng)網(wǎng)絡(luò)
循環(huán)神經(jīng)網(wǎng)絡(luò)是一種時(shí)間遞歸神經(jīng)網(wǎng)絡(luò),能夠理解序列中上下文的知識(shí),同樣使用Keras建立網(wǎng)絡(luò),最終模型的準(zhǔn)確率為99.5%,召回率為98.7%。核心代碼:
模型訓(xùn)練:
- def train(train_generator,train_size,input_num,dims_num):
- print("Start Train Job! ")
- start=time.time()
- inputs=InputLayer(input_shape=(input_num,dims_num),batch_size=batch_size)
- layer1=LSTM(128)
- output=Dense(2,activation="softmax",name="Output")
- optimizer=Adam()
- model=Sequential()
- model.add(inputs)
- model.add(layer1)
- model.add(Dropout(0.5))
- model.add(output)
- call=TensorBoard(log_dir=log_dir,write_grads=True,histogram_freq=1)
- model.compile(optimizer,loss="categorical_crossentropy",metrics=["accuracy"])
- model.fit_generator(train_generator,steps_per_epoch=train_size//batch_size,epochs=epochs_num,callbacks=[call])
使用tensorboard對(duì)網(wǎng)絡(luò)的可視化:
八、卷積神經(jīng)網(wǎng)絡(luò)
卷積神經(jīng)網(wǎng)絡(luò)(CNN)相對(duì)于MLP網(wǎng)絡(luò)減少了需要訓(xùn)練的參數(shù)數(shù)量,降低了計(jì)算量,同時(shí)能夠提煉深度特征進(jìn)行分析,這里使用類似于Google VGG的一維卷積神經(jīng)網(wǎng)絡(luò),包含四個(gè)卷積層、兩個(gè)最大池化層、一個(gè)全連接層,最終的準(zhǔn)確率為99.5%,召回率為98.3%,核心代碼:
- deftrain(train_generator,train_size,input_num,dims_num):
- print("Start Train Job! ")
- start=time.time()
- inputs=InputLayer(input_shape=(input_num,dims_num),batch_size=batch_size)
- layer1=Conv1D(64,3,activation="relu")
- layer2=Conv1D(64,3,activation="relu")
- layer3=Conv1D(128,3,activation="relu")
- layer4=Conv1D(128,3,activation="relu")
- layer5=Dense(128,activation="relu")
- output=Dense(2,activation="softmax",name="Output")
- optimizer=Adam()
- model=Sequential()
- model.add(inputs)
- model.add(layer1)
- model.add(layer2)
- model.add(MaxPool1D(pool_size=2))model.add(Dropout(0.5))
- model.add(layer3)
- model.add(layer4)
- model.add(MaxPool1D(pool_size=2))
- model.add(Dropout(0.5))
- model.add(Flatten())
- model.add(layer5)
- model.add(Dropout(0.5))
- model.add(output)
- call=TensorBoard(log_dir=log_dir,write_grads=True,histogram_freq=1)
- model.compile(optimizer,loss="categorical_crossentropy",metrics=["accuracy"])
- model.fit_generator(train_generator,steps_per_epoch=train_size//batch_size,epochs=epochs_num,callbacks=[call])
九、總結(jié)
本文介紹了如何使用嵌入式詞向量建立XSS語(yǔ)義識(shí)別模型,并分別使用MLP、循環(huán)神經(jīng)網(wǎng)絡(luò)、卷積神經(jīng)網(wǎng)絡(luò)三種算法檢測(cè)XSS攻擊,三種算法都取得了不錯(cuò)的效果。