【CCF2022】系统访问风险识别

本项目基于百度飞桨PaddlePaddle实现系统访问风险识别

一、赛题背景

随着国家、企业对安全和效率越来越重视,作为安全基础设施之一——统一身份管理(IAM,Identity and Access Management)系统也得到越来越多的关注。 在IAM领域中,其主要安全防护手段是身份鉴别,身份鉴别主要包括账密验证、扫码验证、短信验证、人脸识别及指纹验证等方式。这些身份鉴别方式一般可分为三类,即用户所知(如口令)、所有(如身份证)、特征(如人脸识别及指纹验证)。这些鉴别方式都有其各自的缺点——比如口令,强度高了不容易记住,强度低了又容易丢;又比如人脸识别,做活体验证用户体验不好,静默检测又容易被照片、视频、人脸模型绕过。也因此,在等保2.0中对于三级以上系统要求必须使用两种及以上的鉴别方式对用户进行身份鉴别,以提高身份鉴别的可信度,这种鉴别方式也被称为双因素认证。
对用户来说,双因素认证在一定程度上提高了安全性,但也极大地降低了用户体验。也因此,IAM厂商开始参考用户实体行为分析(UEBA,User and Entity Behavior Analytics)、用户画像等行为分析技术,来探索一种既能确保用户体验,又能提高身份鉴别可信度的方法。而在当前IAM的探索过程中,目前最容易落地的方法是基于规则的行为分析技术,因为它可理解性较高,且容易与其它身份鉴别方式进行联动。
但基于规则的行为分析技术局限性也很明显,首先这种技术是基于经验的,有“宁错杀一千,不放过一个”的特点,其次它也缺少从数据层面来证明是否有人正在尝试窃取或验证非法获取的身份信息,又或者正在使用窃取的身份信息。鉴于此,我们举办这次竞赛,希望各个参赛团队利用竞赛数据和行业知识,建立机器学习、人工智能或数据挖掘模型,来弥补传统方法的缺点,从而解决这一行业难题。

更多内容请前往比赛官网查看:系统访问风险识别

比赛任务

本赛题中,参赛团队将基于用户历史的系统访问日志及是否存在风险标记等数据,结合行业知识,构建必要的特征工程,建立机器学习、人工智能或数据挖掘模型,并用该模型预测将来的系统访问是否存在风险。

二、数据分析及处理

本赛题数据是从竹云日志库中抽取某公司一定比例的员工从2022年1月到6月的系统访问日志数据,主要涉及认证日志与风险日志数据。部分字段经过一一对应脱敏处理,供参赛队伍使用。其中认证日志是用户在访问应用系统时产生的行为数据,包括用户名、认证时间、认证城市、接入系统、访问URL等关键信息。

该比赛使用的数据已上传至AI Studio:CCF2022-系统访问风险识别比赛数据集

1.数据集文件说明

名称 说明
train.csv 训练集数据
evaluation_public.csv 测试集数据
submit_sample.csv 提交样例数据

2.训练集和测试集变量含义说明

变量名称 业务含义 说明
id 样本ID
user_name 用户名 若该变量为空,则说明该条日志为用户登录系统前产生
department 用户所在部门
ip_transform 认证IP(加密后) 真实认证IP与加密字符一一对应脱敏处理
device_num_transform 认证设备号(加密后) 真实认证设备号与加密字符一一对应脱敏处理
browser_version 浏览器版本
browser 浏览器
os_type 操作系统类型
os_version 操作系统版本
op_datetime 认证日期时间
http_status_code HTTP类型码
op_city 认证城市
log_system_transform 接入系统(加密后) 真实接入系统与加密字符一一对应脱敏处理
url 访问URL
op_month 认证月份
is_risk 是否存在风险 1:有风险;0:无风险。仅train.csv 数据包含该字段

数据处理

import pandas as  pd
import numpy as np
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
%matplotlib inline


plt.rcParams['font.sans-serif'] = ['FangSong'] # 指定默认字体  
plt.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示问题`
train = pd.read_csv('data/data167041/train.csv', encoding='utf-8')
print("在训练集中,共有{}条数据,其中每条数据有{}个特征".format(train.shape[0], train.shape[1]))
test  = pd.read_csv('data/data167041/evaluation_public.csv', encoding='utf-8')
print("在测试集中,共有{}条数据,其中每条数据有{}个特征".format(test.shape[0], test.shape[1]))
df = pd.concat([train, test])
df.info()
#统计每类数据对应的平均风险概率
for f in ['user_name', 'department', 'ip_transform', 'device_num_transform', 'browser_version', 'browser', 'os_type', 'os_version', 'ip_type',
    'op_city', 'log_system_transform', 'url']:
    
    for v in df[f].unique():
        print(f, v, df[df[f] == v]['is_risk'].mean())
    print('*'*50)
    
# 查询包含Nan值的行
df[df.isnull().T.any()]
df.describe()
df.info()
df['op_datetime'] = pd.to_datetime(df['op_datetime'])
df['hour'] = df['op_datetime'].dt.hour
df['weekday'] = df['op_datetime'].dt.weekday
df['year'] = df['op_datetime'].dt.year
df['month'] = df['op_datetime'].dt.month
df['day'] = df['op_datetime'].dt.day
#去掉不用的特征
df.drop(columns = 'op_datetime', inplace=True)
df.drop(columns = 'op_month', inplace=True)
#数据编码
from sklearn.preprocessing import LabelEncoder

for feat in ['user_name', 'department', 'ip_transform', 'device_num_transform', 'browser_version','log_system_transform', 'op_city','browser', 'os_type', 'os_version', 'ip_type',
     'url']:
    lab = LabelEncoder()
    df[feat] = lab.fit_transform(df[feat])
#填充空值
for feat in [item for item in df.columns if item != 'is_risk']:
    df[feat].fillna(df[feat].median(), inplace=True)
df.corr()['is_risk']
features = [item for item in df.columns if item != 'is_risk']
traindata = df[~df['is_risk'].isnull()].reset_index(drop=True)
testdata = df[df['is_risk'].isnull()].reset_index(drop=True)
data_X = traindata[features].values[:40000]
data_Y = traindata['is_risk'].values[:40000].astype(int).reshape(-1, 1)
data_X_test = traindata[features].values[40000:]
data_Y_test = traindata['is_risk'].values[40000:].astype(int).reshape(-1, 1)
testdata = testdata[features].values
# 归一化
from sklearn.preprocessing import MinMaxScaler
mm = MinMaxScaler()
data_X = mm.fit_transform(data_X)
data_X_test = mm.fit_transform(data_X_test)
testdata = mm.fit_transform(testdata)
print(data_X.shape)
print(data_X_test.shape)
print(testdata.shape)

三、模型组网

使用飞桨PaddlePaddle进行组网

import random
import paddle

seed = 1234
# 设置随机种子 固定结果
def set_seed(seed):
    np.random.seed(seed)
    random.seed(seed)
    paddle.seed(seed)

set_seed(seed)

import paddle
import paddle.nn as nn

# 定义动态图
class Classification(nn.Layer):
    def __init__(self):
        super(Classification, self).__init__()
        self.drop = nn.Dropout(p=0.25)
        self.fc1 = nn.Linear(19, 32)
        self.fc2 = nn.Linear(32, 16)
        self.fc3 = nn.Linear(16, 2)
        self.fc4 = nn.Linear(2, 1)
        self.sig = nn.Sigmoid()
    
    # 网络的前向计算函数
    def forward(self, inputs):
        x = self.fc1(inputs)
        x = self.fc2(x)
        x = self.fc3(x) 
        x = self.drop(x)
        x = self.fc4(x)
        pred  = self.sig(x)
        return pred

四、配置参数及训练

记录日志

# 定义绘制训练过程的损失值变化趋势的方法draw_train_process
train_nums = []
train_costs = []
def draw_train_process(iters,train_costs):
    title="training cost"
    plt.title(title, fontsize=24)
    plt.xlabel("iter", fontsize=14)
    plt.ylabel("cost", fontsize=14)
    plt.plot(iters, train_costs,color='red',label='training cost') 
    plt.grid()
    plt.show()

模型训练

import paddle.nn.functional as F
y_preds = []
labels_list = []
BATCH_SIZE =40
train_data = data_X
train_data_y = data_Y
test_data = data_X_test
test_data_y = data_Y_test
def train(model):
    print('start training ... ')
    # 开启模型训练模式
    model.train()
    EPOCH_NUM = 9
    train_num = 0
    scheduler = paddle.optimizer.lr.CosineAnnealingDecay(learning_rate=0.0025, T_max=int(traindata.shape[0]/BATCH_SIZE*EPOCH_NUM), verbose=False)
    optimizer = paddle.optimizer.Adam(learning_rate=scheduler, parameters=model.parameters())
    #optimizer = paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters())
    for epoch_id in range(EPOCH_NUM):
        # 在每轮迭代开始之前,将训练数据的顺序随机的打乱
        np.random.shuffle(train_data)
        # 将训练数据进行拆分,每个batch包含8条数据
        mini_batches = [np.append(train_data[k: k+BATCH_SIZE], train_data_y[k: k+BATCH_SIZE], axis = 1) for k in range(0, len(train_data), BATCH_SIZE)]
        for batch_id, data in enumerate(mini_batches):
            features_np = np.array(data[:, :19], np.float32)
            labels_np = np.array(data[:, -1:], np.float32)

            features = paddle.to_tensor(features_np)
            labels = paddle.to_tensor(labels_np)
            #前向计算
            y_pred = model(features)
            #使用二分类损失
            cost = F.binary_cross_entropy(y_pred, labels)
            train_cost = cost.numpy()
            #反向传播
            cost.backward()
            #最小化loss,更新参数
            optimizer.step()
            # 清除梯度
            optimizer.clear_grad()
            if batch_id % 1000 == 0 and epoch_id % 1 == 0:
                print("Pass:%d,Cost:%0.5f"%(epoch_id, train_cost))

            train_num = train_num + BATCH_SIZE
            train_nums.append(train_num)
            train_costs.append(train_cost)
def predict(model):
    print('start evaluating ... ')
    model.eval()
    outputs = []
    mini_batches = [np.append(test_data[k: k+BATCH_SIZE], test_data_y[k: k+BATCH_SIZE], axis = 1) for k in range(0, len(test_data), BATCH_SIZE)]
    for data in mini_batches:
        features_np = np.array(data[:, :19], np.float32)
        features = paddle.to_tensor(features_np)
        pred = model(features)
        #out = paddle.argmax(pred, axis=1)
        outputs.extend(pred.numpy())
    return outputs
model = Classification()
train(model)
draw_train_process(train_nums, train_costs)

模型评估

from sklearn import metrics 
from sklearn.metrics import roc_auc_score,roc_curve, auc
outputs = predict(model)
test_data_y = test_data_y.reshape(-1, )
outputs = np.array(outputs)

print('roc_auc_score', roc_auc_score(test_data_y,outputs))

fpr, tpr, threshold = roc_curve(test_data_y,outputs)   
roc_auc = auc(fpr,tpr)
plt.plot(fpr, tpr, 'b', label='AUC = %0.2f' % roc_auc)
plt.legend(loc='lower right')
plt.plot([0, 1], [0, 1], 'r--')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.show()

五、保存预测结果

模型预测:

predict_result=[]
for infer_feature in testdata:
    #print(infer_feature.shape)
    infer_feature = paddle.to_tensor(np.array(infer_feature, dtype='float32'))
    result = model(infer_feature)
    # print(result)
    predict_result.append(result.numpy()[0])

将结果写入.CSV文件中:

import os
import pandas as pd

id_list = [item for item in range(0, 25710)]
label_list = []
csv_file = 'submission.csv'
for item in range(len(id_list)):
    label = format(predict_result[item],'.1f')
    label_list.append(label)

data = {'id':id_list, 'is_risk':label_list}
df = pd.DataFrame(data)
df.to_csv(csv_file, index=False, encoding='utf8')

提交结果文件,分数如下:
比赛提交地址
在这里插入图片描述

六、涨点方法

  1. 可以尝试加入数据集的构造特征
  2. 可以考虑修改模型来提高模型准确度

此文章为搬运
原项目链接

Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐