美文网首页AI奋斗之路机器学习机器学习与数据挖掘
用户浏览器行为分析引入机器学习k-means算法初探

用户浏览器行为分析引入机器学习k-means算法初探

作者: 弗拉明哥 | 来源:发表于2017-07-10 00:44 被阅读547次

    《WestWorld》第一季第二集有一句很有意思的台词:游客William来到西部世界公园,遇到一个美女接待员,但无法区分对方是否是人类,于是产生了如下对话:


    对于很多编写机器行为脚本的攻击者而言,这可能是他们非法行为背后的逻辑——当你无法识别的时候,我还算非法吗?
    其实非法永远是非法,作为一个网站维护人员,你需要做的就是将它识别出来

    以浏览器行为为例,基于哪些特征量,用何种技术,如何准确识别,都不是那么容易的事情,因为机器行为是变化且持续的。唯一容易的事情,可能是处理方式上——不论是弹验证码还是直接阻断请求,处理掉就好了,而实在不必像剧中William对待Dolores那样,“此情无计可消除”。

    笔者公司在浏览器人机行为识别上,目前的做法是基于鼠标&键盘事件分析:例如在浏览器不刷新的前提下,捕获鼠标历史坐标点、移动线路情况、鼠标键盘点击的次数等,看这些指标:

    • 是否不满足设定阈值
    • 是否在黑名单中

    但是各有各的问题:

    • 阈值无法动态变化,只能人工检测到异常,再手工去后台调整
    • 脚本更换数据,可能就不在黑名单了

    可以说是非常尴尬了。

    但这种做法依然有它的合理之处:每次登录的鼠标轨迹完全一样,可以说100%是机器;从开始捕获到验证通过时间小得惊人(例如<0.1s),是机器的概率,也非常之大(也许98%?)...总而言之,这种做法可以概括为:通过开发人员的“拍脑袋”,来想象一些“不像人类操作”的场景,给出一个“自己认为的人类极限值”,从而“描述并阻止机器行为”。

    这种做法,绝对是可以杜绝部分机器行为渗入的——因为正常人确实干不出这事。但是一旦你的脑袋拍歪了,或者“想象力枯竭”,那么这便是漏洞所在。

    因此笔者在思考,能否利用机器学习中的聚类分析,对用户的浏览器行为进行一定程度辅助判定?

    上图来自极验官网“产品特性”截图。很明显,人机行为在一定数据量级下,是存在边界的。从图的分类效果上看,似乎是用到了支持向量机(SVM)

    当然,不排除这只是一张产品宣传效果图。但笔者更倾向于不是。因为很久之前在InfoQ看过一个新浪安全团队的视频:反机器人行为系统漫谈,在16:38秒左右有提到一个“幂律分布”的概念,有兴趣的读者可以一看。是否可以证明大部分非法的请求是存在一些特征,从而区别于其他请求的。

    所以,通过机器学习/对人的行为特征进行大量的分析,建立安全模型去区分人与机器程序理论可行:利用深度学习构建的神经网络是可以不断地自主学习的,在不断的验证过程中不断的学习新的特征分析。

    在此笔者以K-means聚类算法做了一些测试。

    聚类和分类在机器学习中是两个概念。

    • 分类是我们根据已知的一些样本(包括属性与类标号)来得到分类模型(即得到样本属性与类标号之间的函数),然后通过此目标函数来对只包含属性的样本数据进行分类。属于supervised learning(监督学习)。
    • 聚类则事先并不知道任何样本的类别标号,希望通过某种算法来把一组未知类别的样本划分成若干类别,聚类的时候,我们并不关心某一类是什么,我们需要实现的目标只是把相似的东西聚到一起,这在机器学习中被称作 unsupervised learning (无监督学习)

    以本文讨论的案例为例。如果我们的训练样本里面,包含人机识别的结果,且正确率100%,那么可以基于分类算法,如SVM,得到一个分类器函数,对未来的新数据进行人机判定;而如果这个训练样本里,不包含人机识别结果,或者结果并不准确呢?那我们可以通过选取对行为最有影响的特征量做聚类分析

    公司目前的人机识别系统并非100%正确率,但80%还是能保证。因此正好可以用其结果和聚类结果做一个对比,关注两点即可:

    • 各自识别出的人类行为和机器行为数量
    • 聚类结果和公司人机识别结果的一致率是多少

    算法选择##

    以人机识别为例,红色是恶意程序,绿色是正常用户。我们用肉眼可以一眼看出,有两个分类。但如何让机器看出?


    而这就是K-means要解决的问题:
    输入:聚类个数k,以及包含 n个数据对象的数据库。
    输出:满足方差最小标准的k个聚类。


    K-means算法是很典型的基于距离的聚类算法,采用距离作为相似性的评价指标,即认为两个对象的距离越近,其相似度就越大。该算法认为簇是由距离靠近的对象组成的,因此把得到紧凑且独立的簇作为最终目标。

    直接上图更容易理解:

    K-Means 算法概要
    从上图中可以看到,A,B,C,D,E是五个在图中点。而灰色的点是种子点,也就是用来找点群的点。以人机识别为例。假定结果只有人和机器,那么就有两个种子点,所以K=2。则:
    • 随机在图中取K(这里K=2)个种子点
    • 然后对图中的所有点求到这K个种子点的距离,假如点Pi离种子点Si最近,那么Pi属于Si点群。(上图中,可以看到A,B属于上面的种子点,C,D,E属于下面中部的种子点)
    • 接下来,移动种子点到属于他的“点群”的中心。(见图上的第三步)
    • 然后重复第2)和第3)步,直到,种子点没有移动(可以看到图中的第四步上面的种子点聚合了A,B,C,下面的种子点聚合了D,E)

    环境准备##

    • Anaconda3(包含机器学习常用lib的python sdk,这里直接用python3)
    • JetBrains PyCharm Community Edition (python ide)
    • 测试数据
      测试数据选用了4个维度,共4w条数据:
      1.鼠标点击次数,以下记为mc
      2.键盘点击次数,以下记为kc
      3.鼠标移动次数,以下记为mmc
      4.最近三次鼠标的坐标点(x1,y1),(x2,y2),(x3,y3),以下记为m3p。这三个点各自独立作为维度,笔者认为意义不大(单个x or y无法反映其特征),因此取了其构成三角形之面积(最近一次提交上送的轨迹大多差不多,则面积不该过于突兀)。

    另,由于数据来源于公司,出于保密考虑,不贴出具体数据和格式,只贴笔者原创的测试代码。

    测试案例##

    笔者对维度4的引入效果持疑,因此做有无维度4两类测试,看哪种更接近公司人机识别系统结果。

    1. 只选前3个维度
    
    # -*- coding: utf-8 -*-
    # 导入相应的包
    from pyexcel_xls import get_data
    import re
    from sklearn.cluster import KMeans
    import scipy.io as sio
    from mpl_toolkits.mplot3d import Axes3D
    import matplotlib.pyplot as plt
    
    
    xls_data = get_data(r"test.xlsx")
    # print ("Get data type:", type(xls_data))
    # print (xls_data['ub'])
    
    all_num = len(xls_data['ub']);
    ub_dim_list = [[] for i in range(all_num)]
    # print(ub_dim_list)
    ub_res_list = []
    count = 0
    
    for row_data in xls_data['ub']:
        # print (row_data[0])
        ub_data = eval(row_data[0])
    
        ub_res_list.append(row_data[1])
        # ub_dim_list[count].append(polyArea(mp_dim_list))
        ub_dim_list[count].append(ub_data['kc'])
        ub_dim_list[count].append(ub_data['mc'])
        ub_dim_list[count].append(ub_data['mmc'])
    
        count += 1
    # print (ub_dim_list)
    # print('人机识别结果:', ub_res_list)
    
    # 聚类,2个聚簇
    clf = KMeans(n_clusters=2)
    y_pred = clf.fit_predict(ub_dim_list)
    y_pred = y_pred.tolist()
    # print('机器学习结果:', y_pred)
    
    
    import numpy as np
    from matplotlib import pyplot as plt
    from scipy import io as spio
    from sklearn.decomposition import pca
    from sklearn.preprocessing import StandardScaler
    
    # '''归一化数据并作图'''
    scaler = StandardScaler()
    scaler.fit(ub_dim_list)
    x_train = scaler.transform(ub_dim_list)
    print(x_train)
    # '''拟合数据'''
    K=3 # 要降的维度
    model = pca.PCA(n_components=K).fit(x_train)   # 拟合数据,n_components定义要降的维度
    print(model)
    Z = model.transform(x_train)    # transform就会执行降维操作
    print(Z)
    # print(Z[:,0])
    
    fig = plt.figure()
    ax_pca = fig.add_subplot(1, 1, 1, projection='3d')
    ax_pca.view_init(elev=10., azim=11)
    ax_pca.scatter(Z[:,0], Z[:,1], Z[:,2], c=y_pred)
    
    # '''拟合数据'''
    K=2 # 要降的维度
    model = pca.PCA(n_components=K).fit(x_train)   # 拟合数据,n_components定义要降的维度
    print(model)
    Z = model.transform(x_train)    # transform就会执行降维操作
    print(Z)
    # print(Z[:,0])
    
    fig = plt.figure()
    ax_pca = fig.add_subplot(1, 1, 1)
    ax_pca.scatter(Z[:,0], Z[:,1], c=y_pred)
    
    rjsb_num = [0, 0]
    for i in ub_res_list:
        if (i == 0):
            rjsb_num[0] += 1
        else:
            rjsb_num[1] += 1
    print('人机识别结果: 人类:' + str(rjsb_num[0]) + ',机器:' + str(rjsb_num[1]) + ',机器行为占比:' + '%.2f%%' % (
        rjsb_num[1] / all_num * 100))
    
    ml_num = [0, 0]
    for i in y_pred:
        if (i == 0):
            ml_num[0] += 1
        else:
            ml_num[1] += 1
    print('机器学习结果: 人类:' + str(ml_num[0]) + ',机器:' + str(ml_num[1]) + ',机器行为占比:' + '%.2f%%' % (ml_num[1] / all_num * 100))
    
    right_num = 0
    for i in range(0, all_num):
        if ((ub_res_list[i] == y_pred[i] and ub_res_list[i] == 0) or (ub_res_list[i] != 0 and y_pred[i] == 1)):
           right_num += 1
    
    print('以人机识别为参考系,机器学习正确率:%.2f%%' % (right_num / all_num * 100))
    plt.show()
    
    

    其输出为:

    人机识别结果: 人类:37903,机器:2097,机器行为占比:5.24%
    机器学习结果: 人类:39999,机器:1,机器行为占比:0.00%
    以人机识别为参考系,机器学习正确率:94.75%
    
    测试1降维后3D图 测试1降维后2D图

    这里有点令人惊讶:假定人机识别的结果全部正确,那么聚类的“正确率”高达94.75%!——而实际上它判定出4w条行为样本中,只有1个机器行为。
    造成这一情况的主要原因是,样本中机器行为的数量,可能本来就偏少——因此就算认为这4w条全部是人类行为,正确率都可以高于94.75%

    不能说一定不可能,只能说难以令人信服(数据是完全随机的)。因此决定加入维度4测试

    1. 加入维度4
    
    
    # -*- coding: utf-8 -*-
    # 导入相应的包
    from pyexcel_xls import get_data
    import re
    from sklearn.cluster import KMeans
    import scipy.io as sio
    from mpl_toolkits.mplot3d import Axes3D
    import matplotlib.pyplot as plt
    
    
    # 计算多边形面积
    def getArea(pts):
        s = 0
        _len = len(pts)
        x, y = zip(*pts)
        j = _len - 1
        for i in range(_len):
            s = s + (x[j] + x[i]) * (y[j] - y[i])
            j = i
        return abs(s / 2.)
    
    
    xls_data = get_data(r"test.xlsx")
    # print ("Get data type:", type(xls_data))
    # print (xls_data['ub'])
    
    all_num = len(xls_data['ub']);
    ub_dim_list = [[] for i in range(all_num)]
    # print(ub_dim_list)
    ub_res_list = []
    count = 0
    
    for row_data in xls_data['ub']:
        # print (row_data[0])
        ub_data = eval(row_data[0])
        # print(ub_data['mp'])
        mp_dim_split = re.split(';|,', ub_data['m3p'])
        mp_dim_list = [[] for i in range(3)]
        mp_dim = [0.0, 0.0]
        for i in range(0, len(mp_dim_split)):
            if (i % 2 == 0):
                mp_dim[0] = float(mp_dim_split[i])
                mp_dim[1] = float(mp_dim_split[i + 1])
                mp_dim_list[int(i / 2)].extend(mp_dim)
        # print(mp_dim_list)
        # print(polyArea(mp_dim_list))
        ub_res_list.append(row_data[1])
        ub_dim_list[count].append(getArea(mp_dim_list))
        ub_dim_list[count].append(ub_data['kc'])
        ub_dim_list[count].append(ub_data['mc'])
        ub_dim_list[count].append(ub_data['mmc'])
        count += 1
    # print (ub_dim_list)
    # print('人机识别结果:', ub_res_list)
    
    # 聚类,2个聚簇
    clf = KMeans(n_clusters=2)
    y_pred = clf.fit_predict(ub_dim_list)
    y_pred = y_pred.tolist()
    # print('机器学习结果:', y_pred)
    
    
    import numpy as np
    from matplotlib import pyplot as plt
    from scipy import io as spio
    from sklearn.decomposition import pca
    from sklearn.preprocessing import StandardScaler
    
    '''归一化数据并作图'''
    scaler = StandardScaler()
    scaler.fit(ub_dim_list)
    x_train = scaler.transform(ub_dim_list)
    print(x_train)
    '''拟合数据'''
    K=3 # 要降的维度
    model = pca.PCA(n_components=K).fit(x_train)   # 拟合数据,n_components定义要降的维度
    print(model)
    Z = model.transform(x_train)    # transform就会执行降维操作
    print(Z)
    # print(Z[:,0])
    
    fig = plt.figure()
    ax_pca = fig.add_subplot(1, 1, 1, projection='3d')
    ax_pca.view_init(elev=10., azim=11)
    ax_pca.scatter(Z[:,0], Z[:,1], Z[:,2], c=y_pred)
    
    '''拟合数据'''
    K=2 # 要降的维度
    model = pca.PCA(n_components=K).fit(x_train)   # 拟合数据,n_components定义要降的维度
    print(model)
    Z = model.transform(x_train)    # transform就会执行降维操作
    print(Z)
    # print(Z[:,0])
    
    fig = plt.figure()
    ax_pca = fig.add_subplot(1, 1, 1)
    ax_pca.scatter(Z[:,0], Z[:,1], c=y_pred)
    
    
    
    rjsb_num = [0, 0]
    for i in ub_res_list:
        if (i == 0):
            rjsb_num[0] += 1
        else:
            rjsb_num[1] += 1
    print('人机识别结果: 人类:' + str(rjsb_num[0]) + ',机器:' + str(rjsb_num[1]) + ',机器行为占比:' + '%.2f%%' % (
        rjsb_num[1] / all_num * 100))
    
    ml_num = [0, 0]
    for i in y_pred:
        if (i == 0):
            ml_num[0] += 1
        else:
            ml_num[1] += 1
    print('机器学习结果: 人类:' + str(ml_num[0]) + ',机器:' + str(ml_num[1]) + ',机器行为占比:' + '%.2f%%' % (ml_num[1] / all_num * 100))
    
    right_num = 0
    for i in range(0, all_num):
        if ((ub_res_list[i] == y_pred[i] and ub_res_list[i] == 0) or (ub_res_list[i] != 0 and y_pred[i] == 1)):
           right_num += 1
    
    print('以人机识别为参考系,机器学习正确率:%.2f%%' % (right_num / all_num * 100))
    plt.show()
    
    

    其输出为:

    人机识别结果: 人类:37903,机器:2097,机器行为占比:5.24%
    机器学习结果: 人类:37799,机器:2201,机器行为占比:5.50%
    以人机识别为参考系,机器学习正确率:89.79%
    
    测试2降维后3D图 测试2降维后2D图

    可以看出明显的人机界限。
    在对机器行为的识别比例上,两者相差无几;只是有10%左右(大约4k条)的数据判定,两者看法是不一致的。

    那么究竟是谁错了?目前还不得而知——这些数据来自浏览器的另一端,本身不会自带label说i'm from robot。但今后可以在测试环境,基于人的行为,同时基于自己编写的机器脚本,来做一次label确凿的比较。而如果是这样,就可以不仅于局限聚类分析了,一些有监督学习的分类算法也可以引入尝试。

    另外单靠这种算法也并非万能的——攻击者也可以利用机器学习,模拟出“更合法的非法行为”。
    未来势必是机器与机器的战争。

    结语

    笔者入坑机器学习时间较短,对一些算法的具体应用场景可能理解不够透彻。但思路上,希望能利用现有行为数据,动态的打造一个“用户行为模型”来强化公司人机识别产品。此文纯属抛砖引玉。希望有经验的朋友多多指正,谢谢。

    相关文章

      网友评论

      • 99774734cf50:你采集的是什么数据?
      • bettyhubu:明哥不愧是大学写小说的人,博客写的都很有趣味性。。点赞,多分享点人工智能相关的东西呀!最近在了解。
        弗拉明哥:嗯毕竟是写过言情小说的人:hushed: ...人工智能/机器学习吧,我也是新手练级中... 不过想深入了解的话,建议先不要太拘泥于算法本身,不然会晕的。了解各种算法是解决什么问题的,然后用工程的方式(如本文)先做,在这个过程中再反过来研究每一步(如api、参数)的意义。这样可能有助于保持兴趣。

      本文标题:用户浏览器行为分析引入机器学习k-means算法初探

      本文链接:https://www.haomeiwen.com/subject/ryfehxtx.html