基于大数据和机器学习的Web异常参数检测系统Demo实现

一、前言

如何在网络安全领域利用数据科学解决安全问题一直是一个火热的话题,讨论算法和实现的文章也不少。前段时间看到楚安的文章《数据科学在Web威胁感知中的应用》,其中提到如何用隐马尔可夫模型(HMM)建立web参数模型,检测注入类的web攻击。获益匪浅,遂尝试用python实现该算法,并尝试在大数据环境下的部署应用。

二、算法一般过程

隐马尔可夫模型是一个统计模型,可以利用这个模型解决三类基本问题:

Ø  学习问题:给定观察序列,学习出模型参数

Ø  评估问题:已知模型参数,评估出观察序列出现在这个模型下的概率

Ø  解码问题:已知模型参数和给出的观察序列,求出可能性最大的隐藏状态序列

这里我们是要解决前两类问题,使用白样本数据学习出模型和参数基线,计算检测数据在该模型下出现的可能性,如果得分低于基线就可以认为这个参数异常,产出告警。算法可分为训练过程和检测过程,算法本身我这里不在细说(可参见前言中的文章或兜哥的文章),这里重点讲一下参数的抽取和泛化。

 

参数的抽取

对http请求数据进行拆解,提取如下参数,这部分的难点在于如何正确的识别编码方式并解码:

Ø  GET、POST、Cookie请求参数

Ø  GET、POST、Cookie参数名本身

Ø  请求的URL路径

Ø  http请求头,如Content_type、Content-Length(对应strust2-045)

参数泛化

需要将参数值泛化为规律性的观测经验,并取字符的unicode数值作为观察序列,泛化的方法如下:

Ø  大小写英文字母泛化为”A”,对应的unicode数值为65

Ø  数字泛化为”N”,对应的unicode数值为78

Ø  中文或中文字符泛化为“C”,对应的unicode数值为67

Ø  特殊字符和其他字符集的编码不作泛化,直接取unicode数值

Ø  参数值为空的取0

三、系统架构

在训练过程中要使用尽可能多的历史数据进行训练,这显然是一个批(batch)计算过程;在检测过程中我们希望能够实时的检测数据,及时的发现攻击,这是一个流(streaming)计算过程。典型的批+流式框架如Cisco的Opensoc使用开源大数据架构,kafka作为消息总线,Storm进行实时计算,Hadoop存储数据和批量计算。但是这样的架构有一个缺点,我们需要维护Storm和MapReduce两套不同的代码。考虑到学习成本,使用Spark作为统一的数据处理引擎,即可以实现批处理,也可以使用spark streaming实现近实时的计算。

系统架构如上图,需要在spark上运行三个任务,sparkstreaming将kafka中的数据实时的存入hdfs;训练算法定期加载批量数据进行模型训练,并将模型参数保存到Hdfs;检测算法加载模型,检测实时数据,并将告警保存到ES。

四、Spark简介

Apache Spark是一个快速通用的大数据计算框架,由Scala语言实现,同时提供Java、python、R语言的API接口。相比于Hadoop的Mapreduce,Spark可以实现在内存中计算,具有更高的计算速度,并且spark streaming提供流数据计算框架,以类似批处理的方式处理流数据。

RDD

RDD是Spark中抽象的数据结构类型,是一个弹性分布式数据集,数据在Spark中被表示为RDD。RDD提供丰富的API接口,实现对数据的操作,如map、flatmap、reduce、filter、groupby等等。

DStream

DStream(离散数据流)是Spark Streaming中的数据结构类型,它是由特定时间间隔内的数据RDD构成,可以实现与RDD的互操作,Dstream也提供与RDD类似的API接口。

DataFrame

DataFrame是spark中结构化的数据集,类似于数据库的表,可以理解为内存中的分布式表,提供了丰富的类SQL操作接口。

五、数据采集与存储

获取http请求数据通常有两种方式,第一种从web应用中采集日志,使用logstash从日志文件中提取日志并泛化,写入Kafka(可参见兜哥文章);第二种可以从网络流量中抓包提取http信息。我这里使用第二种,用python结合Tcpflow采集http数据,在数据量不大的情况下可稳定运行。

数据采集

与Tcpdump以包单位保存数据不同,Tcpflow是以流为单位保存数据内容,分析http数据使用tcpflow会更便捷。Tcpflow在linux下可以监控网卡流量,将tcp流保存到文件中,因此可以用python的pyinotify模块监控流文件,当流文件写入结束后提取http数据,写入Kafka,Python实现的过程如下图。

核心代码:

#子进程,处理数据到kafka
queue = Queue()

  threadKafka=Process(target=processKafka,args=(queue,options.kafka,options.topic))
  threadKafka.start()

#子线程,开启并监控TCPFLOWtempDir=tempfile.mkdtemp()
threadPacp=threading.Thread(target=processPcap,args=(tempDir,tcpFlowPath,tcpflow_args))
threadPacp.start()
#主进程,监控文件并生成数据
wm=pyinotify.WatchManager()

wm.add_watch(tempDir,pyinotify.ALL_EVENTS)

eventHandler=MonitorFlow(queue)

notifier=pyinotify.Notifier(wm,eventHandler)

notifier.loop()

数据存储

开启一个SparkStreaming任务,从kafka消费数据写入Hdfs,Dstream的python API没有好的入库接口,需要将Dstream的RDD转成DataFrame进行保存,保存为json文件。

核心代码:

topic = {in_topic: in_topic_partitions}

#从kafka获取数据生成Dstream

dstream = KafkaUtils.createStream(ssc, zookeeper,app_conf["app_name"], topic)
dstream = dstream.map(lambda record: json.loads(record[1]))
dstream.foreachRDD(lambda rdd: self.save(rdd))

#将RDD转成DataFrame存入Hdfs

def save(self, rdd):
    if rdd.take(1):
        df = sqlcontext.createDataFrame(rdd)
        df.write.json(app_conf["savedir"], mode="append")
    else:
        pass

六、算法实现

抽取器(Extractor)

抽取器实现原始数据的参数提取和数据泛化,传入一条json格式的http请求数据,可以返回所有参数的id、参数类型、参数名、参数的观察状态序列。

代码示例:

class Extractor(object):
    def __init__(self,data):
        self.parameter={}
        self.data=data
        self.uri = urllib.unquote(data["uri"].encode("utf-8"))
        self.path = decode(get_path(self.uri))
        self.payload = get_payload(self.uri).strip("?")
        self.get_parameter()

#提取post参数
def post(self):
    post_data=urllib.unquote(urllib.unquote(self.data["data"]))
    content_t=self.data["content_type"]

#提取urlencode编码的参数
   def ex_urlencoded(post_data):
        for p in post_data.split("&"):
            p_list = p.split("=")
            p_name = p_list[0]
            if len(p_list) > 1:
                p_value = reduce(operator.add, p_list[1:])
                 #取md5作为参数id
                p_id = get_md5(self.data["host"] + self.path + decode(p_name) + self.data["method"])
                p_state = self.get_Ostate(p_value)
                p_type = "post"
                yield (p_id, p_state, p_type, p_name)

#提取json格式的参数
    def ex_json(post_data):
        post_data=json.loads(post_data)
        for p_name,p_value in post_data.items():
            p_id = get_md5(self.data["host"] + self.path + decode(p_name) + self.data["method"])
            p_state=self.get_Ostate(str(p_value))
            p_type="post"
            yield (p_id, p_state, p_type, p_name)

训练器(Trainer)

训练器完成对参数的训练,传入参数的所有观察序列,返回训练好的模型和profile,HMM模型使用python下的hmmlearn模块,profile取观察序列的最小得分。

核心代码:

class Trainer(object):
    def __init__(self,data):
        self.p_id=data["p_id"]
        self.p_state=data["p_states"]
    def train(self):
        Hstate_num=range(len(self.p_state))
        Ostate_num=range(len(self.p_state))
        Ostate = []
        for (index,value) in enumerate(self.p_state):
            Ostate+=value     #观察状态序列
            Hstate_num[index]=len(set(np.array(value).reshape(1,len(value))[0]))
            Ostate_num[index]=len(value)
        self.Ostate=Ostate
        self.Hstate_num=Hstate_num
        self.n=int(round(np.array(Hstate_num).mean()))#隐藏状态数
        model = GaussianHMM(n_components=self.n, n_iter=1000, init_params="mcs",covariance_type="full")
        model.fit(np.array(Ostate),lengths=Ostate_num)

#计算基线
    def get_profile(self):
        scores=np.array(range(len(self.p_state)),dtype="float64")
        for (index,value) in enumerate(self.p_state):
            scores[index]=self.model.score(value)
        self.profile=float(scores.min())
        self.scores=scores

训练任务

Spark训练任务抽取所有http请求数据的参数,并按照参数ID分组,分别进行训练,将训练模型保存到Hdfs。

核心代码:      

 #读取原始数据     
df =sqlcontext.read.json(self.app_conf["data_dir"])     
    rdd=df.toJSON()

#过滤出请求数据
    p_rdd=rdd.filter(self.filter).cache()

#抽取数据参数
    p_rdd=p_rdd.flatMap(self.extract).cache()
    p_list=p_rdd.collect()
    p_dict={}

#按照参数ID分组
    for p in p_list:
        if p.keys()[0] not in p_dict.keys():
            p_dict[p.keys()[0]]={}
            p_dict[p.keys()[0]]["p_states"]=[p.values()[0]["p_state"]]
            p_dict[p.keys()[0]]["p_type"]=p.values()[0]["p_type"]
            p_dict[p.keys()[0]]["p_name"] = p.values()[0]["p_name"]
        p_dict[p.keys()[0]]["p_states"].append(p.values()[0]["p_state"])
    for key in p_dict.keys():
        if len(p_dict[key]["p_states"]) <self.app_conf["min_train_num"]:
            p_dict.pop(key)
    models=[]
  #训练参数模型
    for p_id in p_dict.keys():
        data={}
        data["p_id"]=p_id
        data["p_states"]=p_dict[p_id]["p_states"]
        trainer=Trainer(data)
        (m,p)=trainer.get_model()
        model = {}
        model["p_id"] = p_id
        model["p_type"]=p_dict[p_id]["p_type"]
        model["p_name"] = p_dict[p_id]["p_name"]
        model["model"] = pickle.dumps(m)
        model["profile"] = p
        models.append(model)
        logging.info("[+]Trained:%s,num is %s"%(p_id,trained_num))
        trained_num+=1

#保存模型参数到Hdfs,保存为Json文件
    model_df=sqlcontext.createDataFrame(models)
    date=time.strftime("%Y-%m-%d_%H-%M")
    path="hdfs://%s:8020%smodel%s.json"%(self.app_conf["namenode_model"],self.app_conf["model_dir"],date)
    model_df.write.json(path=path)

检测任务

Spark Streaming检测任务实时获取kafka流数据,抽取出数据的参数,如果参数有训练模型,就计算参数得分,小于基线输出告警到Elasticsearch。

核心代码:

#获取模型参数
    model_data = sqlcontext.read.json(self.app_conf["model_dir"]).collect()
    model_keys=[0]*len(model_data)
    for index,model_d in enumerate(model_data):
        model_keys[index]=model_d["p_id"]
    ssc=StreamingContext(sc,20)
    model_data = ssc._sc.broadcast(model_data)
    model_keys = ssc._sc.broadcast(model_keys)
    zookeeper = self.app_conf["zookeeper"]
    in_topic = self.app_conf["in_topic"]
    in_topic_partitions = self.app_conf["in_topic_partitions"]
    topic = {in_topic: in_topic_partitions}

#获取kafka数据
    dstream = KafkaUtils.createStream(ssc, zookeeper, self.app_conf["app_name"], topic)

#过滤出请求数据
    dstream=dstream.filter(self.filter)

#对每条数据进行检测
    dstream.foreachRDD(
       lambda rdd: rdd.foreachPartition(
           lambda iter:self.detector(iter,model_data,model_keys)
       )
    )
    ssc.start()
    ssc.awaitTermination()
def detector(self, iter,model_data,model_keys):
    es = ES(self.app_conf["elasticsearch"])
    index_name = self.app_conf["index_name"]
    type_name = self.app_conf["type_name"]
    model_data=model_data.value
    model_keys=model_keys.value
    for record in iter:
        record=json.loads(record[1])
        try:

  #抽取数据参数

parameters = Extractor(record).parameter
            for (p_id, p_data) in parameters.items():
                if p_id in model_keys:
                    model_d = model_data[model_keys.index(p_id)]
                    model = pickle.loads(model_d.model)
                    profile = model_d.profile
                    score = model.score(np.array(p_data["p_state"]))
                    if score < profile:

    #小于profile的参数数据输出告警到es

                       alarm = ES.pop_null(record)
                        alarm["alarm_type"] = "HmmParameterAnomaly "
                        alarm["p_id"] = p_id
                        alarm["p_name"] = model_d.p_name
                        alarm["p_type"] = model_d.p_type
                        alarm["p_profile"] = profile
                        alarm["score"] = score
                        es.write_to_es(index_name, type_name, alarm)
        except (UnicodeDecodeError, UnicodeEncodeError):

 

七、总结

所有的机器学习算法都大致可分为训练、检测阶段,基于HMM的web参数异常检测是其中的典型代表,本文尝试将机器学习算法在大数据环境下使用,所有用到的代码都会在Github上公开(其实数据抽取部分并不完美,欢迎提出好的建议)。

代码地址:https://github.com/SparkSharly/Sharly

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要设计一个基于机器学习Web异常流量检测系统,需要考虑以下几个步骤: 1. 数据收集: 首先需要收集正常和异常的网络流量数据。这可以通过在网络边界处放置流量捕获设备来实现。 2. 数据预处理: 收集到的数据需要进行预处理,以便能够输入到机器学习模型中。这可能包括清洗、标准化和采样等步骤。 3. 模型训练: 使用预处理后的数据训练一个机器学习模型,以识别异常流量。可以使用各种算法,如决策树,随机森林,支持向量机等。 4. 模型部署: 将训练好的模型部署到生产环境中,并在实时网络流量中使用模型进行检测。 5. 效果评估: 定期评估模型的性能,并对其进行调整和优化。 ### 回答2: 基于机器学习Web异常流量检测系统是一个用于检测和预测Web应用程序中异常流量的系统。该系统通过监控Web应用程序的网络流量和用户行为,利用机器学习算法进行异常检测和分类,以便及时发现并应对网络攻击、恶意行为和异常访问等安全威胁。 首先,该系统需要对收集到的网络流量和用户行为数据进行预处理和特征工程。这包括数据清洗、缺失值处理、特征提取和选择等。然后,通过监督学习算法,如支持向量机(SVM)、决策树或随机森林等,对数据进行训练和建模。训练集应包括正常流量和已知异常流量的样本,以便系统能够学习到正常和异常模式。 训练完成后,系统可以使用已经训练好的模型来对新的网络流量进行分类和异常检测。当新的网络流量进来时,系统会提取特征并将其输入到模型中进行预测。如果预测结果为正常,流量将被允许通过;如果预测结果为异常,流量将被阻塞或采取其他安全措施。 为了提高系统的准确性和适应性,还可以使用无监督学习算法,如聚类算法或异常检测算法,对未标记的数据进行训练和建模。这样系统可以发现新的未知异常模式,并对其进行分类和检测。 此外,系统还应具备实时监控和警报功能。当检测异常流量或安全威胁时,系统可以发送警报给管理员或相关人员,以便及时采取措施进行应对和修复。 综上所述,基于机器学习Web异常流量检测系统可以帮助企业和组织提高Web应用程序的安全性,减少网络攻击和恶意行为的影响。该系统能够智能化地检测和识别异常流量,并及时采取措施,保护Web应用程序的安全运行。 ### 回答3: 基于机器学习Web异常流量检测系统是一种利用机器学习算法来检测和识别Web应用中异常流量的系统。下面是设计这样一个系统的一些主要步骤和方法。 首先,系统需要收集和存储Web应用的日志数据,包括网络流量数据、访问请求数据等。这些数据将作为训练集用于机器学习模型的训练。 接下来,需要选择合适的机器学习算法。常见的算法包括支持向量机(SVM)、决策树、随机森林等。这些算法可以用于分类和识别Web流量中的异常行为。 在训练模型之前,需要对数据进行预处理。这包括特征选择、特征提取和数据归一化等步骤。特征选择是为了选择最相关的特征,提高模型的准确率。特征提取是将原始数据转换为更有信息量的特征向量。数据归一化是将数据转化为相同的尺度,以便算法的准确性。 然后,使用训练数据集来训练机器学习模型。训练过程中需要调整参数,以获得最佳的模型性能。 完成模型训练后,可以在实时流量中应用该模型进行异常流量检测。将实时的网络流量数据输入到模型中,通过模型预测结果判断是否存在异常行为。 最后,需要对检测结果进行评估和监控。评估可以通过计算模型的准确率、召回率等指标来进行。监控可以实时监测系统的运行状态和检测效果。 综上所述,设计一个基于机器学习Web异常流量检测系统需要考虑数据收集和预处理、机器学习算法的选择和训练、模型的应用和检测结果的评估等步骤。这需要充分理解Web流量特点和机器学习算法的原理,以提高系统的准确性和实时性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值