机器学习

《Web安全之机器学习入门》笔记:第十二章 12.3 隐式马尔可夫算法识别XSS攻击(一)

本文主要是介绍《Web安全之机器学习入门》笔记:第十二章 12.3 隐式马尔可夫算法识别XSS攻击(一),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

本小节通过使用隐式马尔可夫来识别XSS攻击,通过xss白名训练马尔可夫模型,然后用马尔可夫模型测试xss攻击黑名单,这样。单示例隐式马尔可夫在网络安全中的使用。

1、参数建模

对于XSS攻击,首先我们需要对数据进行泛化,比如:

  • [a-zA-Z]泛化为A
  • [0-9]泛化为N
  • [-_]泛化为C
  • 其他字符泛化为T

      根据如上原则admin123泛化为AAAAANNN,root泛化为AAAA。

      如下图所示,url参数wjq_2014为例,对应泛化为AAACNNNN,隐藏序列为S1 S1 S1 S2 S3 S3 S3 S3。

        正常http请求中参数的取值范围是固定的,比如userid字段有字母和特殊字符'-_'组成。以uid为例,可以对其泛化通过三阶段HMM来判断其观察序列,隐藏序列即在S1-S4之间循环转化,这个概率成为转移概率矩阵。同时四个状态都有确定的概率,以观察序列中的A、C、N、T共4个状态展现,这个概率即为发射概率矩阵。HMM建模就是通过学习样本,生成两个矩阵的过程。

  

2、数据处理与特征提取

训练样本即为白名单,可以选择'./good-xss-10000.txt'文件即可,我们来看一下白样本的内容:

源码如下 

    X = [[0]]
    X_lens = [1]

    with open(filename, encoding='utf-8') as f:
        for line in f:
            line=line.strip('\n')
            line=parse.unquote(line)
            vers = etl(line)
            X=np.concatenate( [X,vers])
            X_lens.append(len(vers))

对于样本,首先我们根据第一点提到的泛化规则,需要对数据进行泛化,这里选取的原则如下:

  • [a-zA-Z]泛化为A
  • [0-9]泛化为N
  • [-_]泛化为C
  • 其他字符泛化为T

代码如下所示

def etl(str):
    vers=[]
    for i, c in enumerate(str):
        c=c.lower()
        if   ord(c) >= ord('a') and  ord(c) <= ord('z'):
            vers.append([ord('A')])
        elif ord(c) >= ord('0') and  ord(c) <= ord('9'):
            vers.append([ord('N')])
        elif c in SEN:
            vers.append([ord('C')])
        else:
            vers.append([ord('T')])
    return np.array(vers)

3、训练模型

这里选择三阶段HMM训练白样本

    remodel = hmm.GaussianHMM(n_components=3, covariance_type="full", n_iter=100)
    remodel.fit(X,X_lens)

4、模型验证

(1)白样本测试

这里基于白样本特征,采用与训练相同的方法

    x = []
    y = []
    with open(filename, encoding='utf-8') as f:
        for line in f:
            line=line.strip('\n')
            line=parse.unquote(line)
            vers = etl(line)
            pro = remodel.score(vers)
            x.append(len(vers))
            y.append(pro)

    return x,y

(2)黑样本测试

接下来看一下黑样本’xss-10000.txt’,打开文件查看下具体内容

为了避免中文等字符的干扰,ASCII大于127或者小于32的可以不处理直接跳过

SEN=['<','>',',',':','\'','/',';','"','{','}','(',')']

def ischeck(str):
    if re.match(r'^(http)',str):
        return False
    for i, c in enumerate(str):
        # 排除中文干扰 只处理127以内的字符
        if ord(c) > 127 or ord(c) < 31:
            return False
        if c in SEN:
            return True

    return False

从每条黑样本中提取url参数,通过python接口解决url编码、参数抽取等问题,验证代码如下: 

    x = []
    y = []
    with open(filename, encoding='utf-8') as f:
        for line in f:
            # 切割参数
            result = parse.urlparse(line)
            # url解码
            query = parse.unquote(result.query)
            params = parse.parse_qsl(query, True)
            for k, v in params:
                if ischeck(v) and len(v) >=N :
                    vers = etl(v)
                    pro = remodel.score(vers)
                    x.append(len(vers))
                    y.append(pro)

    return x,y

5、基础函数完整代码

#-*- coding:utf-8 –*-
from urllib import parse
import re
from hmmlearn import hmm
import numpy as np
import matplotlib.pyplot as plt

import joblib
#处理参数值的最小长度
MIN_LEN=6

#其他字符
SEN=['<','>',',',':','\'','/',';','"','{','}','(',')']

def ischeck(str):
    if re.match(r'^(http)',str):
        return False
    for i, c in enumerate(str):
        # 排除中文干扰 只处理127以内的字符
        if ord(c) > 127 or ord(c) < 31:
            return False
        if c in SEN:
            return True

    return False

def etl(str):
    vers=[]
    for i, c in enumerate(str):
        c=c.lower()
        if   ord(c) >= ord('a') and  ord(c) <= ord('z'):
            vers.append([ord('A')])
        elif ord(c) >= ord('0') and  ord(c) <= ord('9'):
            vers.append([ord('N')])
        elif c in SEN:
            vers.append([ord('C')])
        else:
            vers.append([ord('T')])
    #print(vers)
    return np.array(vers)

def train(filename):
    X = [[0]]
    X_lens = [1]

    with open(filename, encoding='utf-8') as f:
        for line in f:
            line=line.strip('\n')
            line=parse.unquote(line)
            vers = etl(line)
            X=np.concatenate( [X,vers])
            X_lens.append(len(vers))

    remodel = hmm.GaussianHMM(n_components=3, covariance_type="full", n_iter=100)
    remodel.fit(X,X_lens)
    joblib.dump(remodel, "xss-train1.pkl")

    return remodel

def test_normal(filename):
    remodel = joblib.load("xss-train1.pkl")
    x = []
    y = []
    with open(filename, encoding='utf-8') as f:
        for line in f:
            line=line.strip('\n')
            line=parse.unquote(line)
            vers = etl(line)
            pro = remodel.score(vers)
            x.append(len(vers))
            y.append(pro)

    return x,y

def test(filename):
    remodel = joblib.load("xss-train1.pkl")
    x = []
    y = []
    with open(filename, encoding='utf-8') as f:
        for line in f:
            # 切割参数
            result = parse.urlparse(line)
            # url解码
            query = parse.unquote(result.query)
            params = parse.parse_qsl(query, True)
            for k, v in params:
                #print('k:',k,'v:',v,'line:',line)
                if ischeck(v) and len(v) >=MIN_LEN :
                    vers = etl(v)
                    pro = remodel.score(vers)
                    x.append(len(vers))
                    y.append(pro)

    return x,y

6、测试对比

        作者配套源码乱七八糟,很多内容与书上写的内容都不一样,包括函数调用参数也没有指明(比如训练样本是哪个文件、测试样本是哪个文件),这部分包括得分pro与阈值T的值设定(书上说10,但是源码-200),也没有写明白,实验结果与书上也不一致。

(1)黑样本测试方法

       这里我使用实验数据,图示对比使用test函数进行对比 测试均使用黑样本测试函数,代码如下

if __name__ == '__main__':
    train('./good-xss-10000.txt')

    x1,y1=test('./good-xss-10000.txt')
    x2,y2=test('./xss-10000.txt')

    fig,ax=plt.subplots()
    ax.set_xlabel('Line Length')
    ax.set_ylabel('HMM Score')
    ax.scatter(x2, y2, color='g', label="xss",marker='v')
    ax.scatter(x1, y1, color='r', label="good",marker='*')
    ax.legend(loc='best')
    plt.show()

白样本10000,黑样本10000个 

11 10100

运行结果如下:

        这是因为绝大多数白样本在parse.parse_qsl函数和ischeck(v) and len(v) >=MIN_LEN

处理后,得到的k和v基本上都是[],这种黑白样本之间的差别,使得大多数白样本使用test函数验证时,基本上都被过滤掉了,一万个就剩下11个左右,约剩下千分之一。

        刚刚使用的是各10000个黑白样本进行测试,这里选择各200000个黑白样本再次测试。

if __name__ == '__main__':
    train('./good-xss-10000.txt')
    x1,y1=test('../data/good-xss-200000.txt')
    x2,y2=test('../data/xss-200000.txt')
    print(len(y1), len(y2))
    fig,ax=plt.subplots()
    ax.set_xlabel('Line Length')
    ax.set_ylabel('HMM Score')
    ax.scatter(x2, y2, color='g', label="xss",marker='v')
    ax.scatter(x1, y1, color='r', label="good",marker='*')
    ax.legend(loc='best')
    plt.show()

如下所示,这20w的白样本和20w的黑样本测试结果中,白样本没过滤的仅128个,比例约千分之一。而黑样本20w也被过滤剩下14752个了,这也侧面说明这种方式不太靠谱。

128 14752

从图示来看,黑样本和没过滤掉的白样本特征也差不太多 

(2)白样本测试对比

       这里我使用实验数据,图示对比使用test_normal函数进行对比 测试均使用和训练相同的白样本测试函数,代码如下

f __name__ == '__main__':
    train('./good-xss-10000.txt')

    x1,y1=test_normal('./good-xss-10000.txt')
    x2,y2=test_normal('./xss-10000.txt')
  
    fig,ax=plt.subplots()
    ax.set_xlabel('Line Length')
    ax.set_ylabel('HMM Score')
    ax.scatter(x2, y2, color='g', label="xss",marker='v')
    ax.scatter(x1, y1, color='r', label="good",marker='*')
    ax.legend(loc='best')
    plt.show()

白样本10000,黑样本10000个 

10000 10000

8.黑白样本测试对比3

if __name__ == '__main__':
    train('./good-xss-10000.txt')

    x1,y1=test_normal('./good-xss-10000.txt')
    x2,y2=test('./xss-10000.txt')
   
    fig,ax=plt.subplots()
    ax.set_xlabel('Line Length')
    ax.set_ylabel('HMM Score')
    ax.scatter(x2, y2, color='g', label="xss",marker='v')
    ax.scatter(x1, y1, color='r', label="good",marker='*')
    ax.legend(loc='best')
    plt.show()

白样本10000,黑样本10100个 

10000 10100

本小节由于实验效果不好,作者配套源码也比较乱,资料不多,调试代码很久,主要是HMM识别效果不算上佳,但是这本书的读书目的也不是为了学完后效果一定很好。尤其是数据集、源码与书也不太一致,学习到基本的方用法即可。

后续如果有深刻的理解,再补充修改这篇笔记。

这篇关于《Web安全之机器学习入门》笔记:第十二章 12.3 隐式马尔可夫算法识别XSS攻击(一)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!