尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

加密恶意流量检测:基于机器学习的全流程项目实战

发布时间:2026/9/26 19:14:24

资讯中心
01
ARTICLE

加密恶意流量检测:基于机器学习的全流程项目实战

加密恶意流量检测:基于机器学习的全流程项目实战
简介面向毕业设计与课程实践场景的机器学习加密恶意流量分析与检测项目提供完整可运行的Python源码和配套文档说明。项目以CTU-13恶意流量和DoH加密DNS流量为数据基础覆盖流量特征提取与相关性分析、Boruta特征筛选、多模型训练对比、结果评估与可视化展示等关键环节。代码内部包含较详细注释文档对实验思路、数据处理过程和结果表现作了说明适合具备基础Python能力的学生快速搭建环境并复现实验。压缩包共217个文件主要包含log日志、html可视化结果页、csv特征与模型结果、py源码、pcap原始数据包、npy特征文件以及图片素材整体大小25.6MB目录层次分明便于按模块学习和二次开发。已有187人学习下载可作为毕业设计、期末大作业或课程设计的高分参考直接用于方案复现、报告撰写与功能拓展。1. 加密流量检测是一个被低估的机器学习落地点加密流量在今天的网络上早已不是少数派HTTPS、TLS 1.3、DoH 一普及传统靠端口、特征字符串做检测的规则引擎基本失效恶意流量开始大摇大摆地藏在加密信道里流动。这个基于机器学习的加密恶意流量分析与检测项目把 CTU-13 僵尸网络流量和 DoH 流量两组标准公开数据拉进来走完了从会话特征提取、相关性过滤、Boruta 特征选择到多模型训练对比、交互式可视化的全流程。它适合两类人一类是正在做 Python 毕设或课程设计需要一份能部署、能答辩、代码带注释的完整项目另一类是刚接触流量安全的同学想用最短路径看清机器学习模型在安全场景里的完整用法。跟着复现一遍你基本就摸清了这类任务的套路。2. 两条数据集线CTU-13 与 DoH 的任务定义和标签体系做加密恶意流量检测第一步不是调模型而是搞清楚数据集里的标签到底代表什么。这个项目没有只押在一份数据上而是拉了两条线CTU-13 和 DoH分别对应两类形态完全不同的加密恶意流量场景。这个选择在答辩时也很讨巧——你能讲清楚为什么需要两种数据来验证方法而不是只在一个数据集上自说自话。2.1 为什么必须同时拿 CTU-13 和 DoH 两个场景CTU-13 是捷克理工大学公开的僵尸网络流量基准集里面包含了 13 个不同的僵尸网络捕获场景流量以会话流flow的形式组织标签维度比较多。它的价值在于僵尸网络流量虽然也走加密信道但恶意行为特征心跳、DGA 域名查询、定向外连会显著改变流的统计形态比如连接持续时间变长、分组到达时间间隔呈现固定节律这些特征恰好是机器学习模型最容易捕捉的。DoH 则是另一回事。DNS over HTTPS 把 DNS 查询整个塞进 TLS 加密流里从报文载荷层面几乎无特征可用只能靠流量行为、分组大小分布、时间间隔统计来鉴别。它比 CTU-13 更接近纯加密场景检测难度更大。两组数据合在一起项目就覆盖了从加密与传统流量混杂到全加密服务的谱系。你的模型如果两边的结果都说得过去说明它不只是在某个数据集上过拟合。在文件组织上doh_corr_features.csv、doh_boruta_features.csv 对应 DoH 线ctu13_corr_features.csv、ctu13_boruta_features.csv 对应 CTU-13 线。corr 后缀文件是相关性过滤后的特征矩阵boruta 后缀文件是 Boruta 特征选择后的特征矩阵后面的模型结果 csv 也是按这两条线分别输出的。2.2 数据怎么读先看形状、列名和标签分布拿到 csv 后的第一件事不是急着训练而是用 pandas 把文件读进来看清行数、列数和标签分布。我一般会这样起手import pandas as pd # 先读 Boruta 筛选后的 CTU-13 特征文件 ctu pd.read_csv(ctu13_boruta_features.csv) print(shape:, ctu.shape) print(columns:, ctu.columns.tolist()) # 自动找到标签列看类别分布 label_col [col for col in ctu.columns if label in col.lower() or class in col.lower()] print(label column:, label_col) print(ctu[label_col[0]].value_counts())这段代码的逻辑很直接先打印矩阵规模和列名确认特征数量和标签列位置再用 value_counts 统计类别分布这一步能帮你判断数据是否平衡。需要注意流特征文件通常是以 NetFlow 格式为底子生成的行数可能上万标签列名在不同数据集里不一样所以用模糊匹配去定位标签列是更稳妥的习惯。DoH 线文件的读取方式完全一样两个文件的区别主要在特征集合上。CTU-13 的特征偏传统流统计——持续时长、收发分组、字节数、标志位分布DoH 的特征则更关注 TLS 握手信息、分组长度分布和 DNS over HTTPS 流的时间结构。2.3 标签体系里的“坑”要先拆干净CTU-13 的标签不只有 benign 和 malicious常见的还有 normal、botnet、background、flashcrowd 这几类。这里最容易翻车的地方是background 代表的是背景噪声流量它既不是恶意行为也不是用户主动产生的正常业务而是网络里的扫描、广播、探测等杂散流量flashcrowd 是闪拥流量也不属于攻击。如果一股脑把 background 当恶意样本塞进去模型学到的就是非正常流量即恶意的错误边界。我处理这类标签时通常会做归并把 botnet 视为恶意样本把 normal 和 flashcrowd 归为正常样本background 选择丢弃或单独建模。如果你做的是二分类模型推荐直接丢弃 background因为它的语义太模糊混进任何一侧都会污染决策边界。DoH 数据集则干净得多通常就是 benign 和 malicious 二分类直接可用。下表是我在复现时梳理的对比你可以拿来做答辩材料对比维度CTU-13DoH场景类型僵尸网络混合流量含加密DNS over HTTPS 纯加密流量标签体系normal / botnet / background / flashcrowdbenign / malicious检测目标识别被控主机的命令与控制行为识别隐藏在 DoH 信道里的恶意查询特征侧重点流统计、连接节奏、上下行比例TLS 元数据、分组长度分布、时间间隔这份表格说来简单但把两类数据的边界讲清楚比直接贴训练代码更能体现你对项目的理解。3. 特征工程三步走从相关性过滤到 Boruta 特征筛选流量数据的维度通常不会低一次捕获能提取出几十个流统计特征但里面真正有效的往往只有三分之一。这个项目的特征工程部分走了三步先做相关性过滤把冗余列砍掉再用 Boruta 算法对剩余特征做重要性筛选最后对选中的特征做业务含义解释。三步下来模型输入维度大幅下降训练速度和可解释性同时提升这也是它能拿高分的关键设计。3.1 相关性过滤先杀掉一批明显冗余的特征加密流量特征之间存在很强的相关性比如总字节数和平均分组长度往往高度相关上行分组数和下行分组数也和流方向占比耦合。直接把所有特征喂给模型树模型还好线性模型会出现系数不稳定更重要的是多个相关特征会稀释特征重要性判断。常见的做法是先计算相关性矩阵把相关系数超过阈值的特征对中挑一个删掉。import pandas as pd import numpy as np df pd.read_csv(doh_corr_features.csv) # 假设标签列在最后一列 X df.drop(columns[df.columns[-1]]) # 计算特征矩阵的相关性绝对值矩阵 corr X.corr().abs() # 取上三角避免重复比对 upper corr.where(np.triu(np.ones(corr.shape), k1).astype(bool)) drop_set set() for col in upper.columns: high_corr_cols [c for c in upper.index if upper.loc[c, col] 0.95] drop_set.update(high_corr_cols) X_reduced X.drop(columnsdrop_set) print(features before:, X.shape[1], after:, X_reduced.shape[1]) print(dropped:, sorted(drop_set))逻辑说明np.triu 取相关矩阵的上三角只保留每个特征对一次比对遍历每一列找出与该列相关系数超过 0.95 的其他特征统一放入待删集合。参数上阈值选 0.9 到 0.95 比较常见0.95 更保守适合特征总量不太多、不想误删有效信息的场景。如果你后续还要跑 Boruta相关性过滤这步前置处理能帮 Boruta 省下大量迭代时间这是我在实操里最推荐的一条流水线。3.2 Boruta 特征选择为什么比树的 feature_importance 更可靠很多同学会直接拿随机森林的 feature_importance 排序来筛特征但这条路有一个隐藏问题树模型对高度相关的特征组会分散重要性导致真正重要的特征排名被拉低。Boruta 的做法更稳它在原始特征旁边生成一批影子特征把原始特征的值随机打乱然后让随机森林在所有特征原始影子上训练比较原始特征的重要性是否显著高于影子特征中的最优者迭代多轮后只有那些反复胜过影子特征的原始特征才会被保留。from boruta import BorutaPy from sklearn.ensemble import RandomForestClassifier X X_reduced.fillna(0).values y df[df.columns[-1]].values # Boruta 内部每轮都要重训森林n_estimators 太大会非常慢 rf RandomForestClassifier( n_estimators200, max_depth8, random_state42, n_jobs-1 ) boruta BorutaPy( estimatorrf, n_estimatorsauto, max_iter50, random_state42, perc100 ) boruta.fit(X, y) kept X_reduced.columns[boruta.support_].tolist() print(Boruta kept:, kept)参数说明n_estimators 使用 auto 会依据特征数量自动确定内部迭代的树数量max_iter50 限制最大迭代轮数避免长时间空转perc100 表示以影子特征的最大值作为比较基线这是最严格的标准如果你想放宽一点可以调到 95 或 90。注意 BorutaPy 的 fit 方法接收的是 numpy 数组所以我用 .values 传入同时在全过程里保留原 DataFrame 的列名方便最终调用 boruta.support_ 来映射哪些列被留下。还有一个实际坑特征矩阵里不能有 NaN我在 fit 前统一 fillna(0)遇到缺失严重的特征建议直接删列。3.3 被保留下来的特征在业务上意味着什么以 DoH 数据集为例Boruta 一轮跑下来通常留下的特征集中在分组长度均值、到达时间间隔方差、上下行分组数比这几类。为什么会是它们因为恶意 DoH 流量多由自动化脚本产生分组节奏均匀、长度分布集中而真实用户的 DNS over HTTPS 查询有很强的随机性和间歇性。CTU-13 场景里被保留下来的往往还有流持续时间、TCP 标志位分布、每流字节数方差——这些特征能捕捉到僵尸网络心跳连接与正常业务流的差异。用表格归纳一下特征类型和业务含义特征类型典型特征恶意流量中的表现时间结构到达时间间隔均值/方差心跳流量呈周期性方差明显偏小分组形态分组长度均值/标准差隧道流量长度分布集中标准差异常方向特征上下行分组数比、方向占比DGA 查询重上行数据窃取重下行连接节奏流持续时间、握手时间失陷主机维持长连接概率更高这些解释是答辩时的加分项也是你在写文档说明时能写出个人手打味道的关键——模型选中的特征不是黑匣子里的随机结果而是能和攻击行为一一对应起来的。4. 模型对比与结果解读别只盯着 accuracy 一个数特征工程做完模型训练本身反而是流水线里最顺手的一环。项目里 doh_boruta_model_result.csv 和 ctu13_boruta_model_result.csv 两个结果文件存的就是不同模型在 Boruta 特征子集上的表现对比。这一章我把训练评估的完整套路拆开讲包括分类器怎么选、评估代码怎么写、结果文件怎么解读。4.1 分类器选型为什么树模型是主力线性模型做对照流量特征普遍存在两个特点一是特征尺度差异极大字节数可能是千级时间间隔是毫秒级二是特征之间存在非线性交互比如小分组 短间隔组合才是恶意行为的信号单独看哪个都不明显。线性模型对尺度敏感需要做标准化而且很难自动捕捉交互效应随机森林和 LightGBM 这类树模型天然适应这种场景。因此主模型推荐随机森林再用逻辑回归做一个基准对照这已是流量安全论文里最常见的组合。随机森林作为主模型还有一个实际优势它对高维稀疏特征不敏感调参压力小默认参数下也能出不错的分数适合毕设这种时间有限的项目。LightGBM 更强但调参成本高如果你不熟悉的细节建议不要作为主模型当作补充对比即可。4.2 训练评估的标准流程以 DoH 的 Boruta 特征文件为例完整的训练评估代码长这样from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report, roc_auc_score from sklearn.preprocessing import LabelEncoder import pandas as pd df pd.read_csv(doh_boruta_features.csv) X df.drop(columns[df.columns[-1]]) y df[df.columns[-1]] # 标签转 0/1树模型需要数值型目标 le LabelEncoder() y le.fit_transform(y) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, stratifyy, random_state42 ) model RandomForestClassifier( n_estimators300, max_depth10, min_samples_leaf2, class_weightbalanced, random_state42, n_jobs-1 ) model.fit(X_train, y_train) y_pred model.predict(X_test) y_prob model.predict_proba(X_test)[:, 1] print(classification_report(y_test, y_pred, digits3)) print(AUC:, round(roc_auc_score(y_test, y_prob), 4))逻辑说明train_test_split 里 stratifyy 保证训练集和测试集的类别比例与原始分布一致这在恶意流量占比偏小的数据集里是必须的否则随机切分很容易让测试集里几乎没有恶意样本。model 里 class_weightbalanced 让模型对少数类做权重补偿解决样本不平衡问题。输出部分classification_report 给出 precision、recall、F1再加一个 AUC 覆盖概率排序质量。这些指标会被汇总写入 doh_boruta_model_result.csv文件在项目里就是一份多模型对比表。你可以把逻辑回归、随机森林、LightGBM 的准确率、精确率、召回率、F1、AUC 各列排开行是模型名。注意 precision 高而 recall 低时说明模型宁缺毋滥只抓最有把握的恶意样本反之 recall 高 precision 低则模型偏激进会把大量正常流量标成恶意。流量安全场景里误报会导致安全运营人员疲于奔命漏报则可能直接造成失陷失窃所以需要结合成本权衡。4.3 结果文件怎么比才公平我的习惯是先把两个结果 csv 读成 DataFrame然后以 F1 和 AUC 两个指标为准做排序而不是看 accuracy。import pandas as pd res pd.read_csv(doh_boruta_model_result.csv) print(res.columns.tolist()) # 以 F1 排序兼顾 AUC res_sorted res.sort_values([F1, AUC], ascendingFalse) print(res_sorted[[model, accuracy, precision, recall, F1, AUC]])在 CTU-13 上因为数据量更大、特征更丰富随机森林和 LightGBM 的 F1 通常会比 DoH 场景高——这不是模型差异而是两个场景的难度本身不同。读结果时先把两个 csv 分开看再用同一个模型在两个场景里的表现做横向对比比直接跨文件比数字更科学。5. 常见问题排查训练时最容易翻车的五个地方流量检测项目跑起来会遇到一堆幺蛾子很多问题不是模型本身导致的而是数据处理和库版本层面的。这一章把我踩过的坑按现象 → 原因 → 解决写清楚你照着排查能省下大半天。5.1 一读数据集就内存溢出现象pd.read_csv 跑 ctu13_corr_features.csv 时进程直接被杀或者内存占用瞬间涨到十几个 G。原因CTU-13 完整数据集如果合并成单张宽表行数可能达到几十万甚至更多默认 read_csv 会把所有列读成 int64/float64内存开销翻倍。解决读取时只加载需要的列并指定更紧凑的 dtype。cols [duration, bytes_in, bytes_out, label] df pd.read_csv(ctu13_corr_features.csv, usecolscols, dtype{ duration: float32, bytes_in: float32, bytes_out: float32 })float32 把精度从 64 位降到 32 位对流量统计特征完全够用内存直接减半。如果你的机器还是吃力把 nrows 参数加上先读 10000 行确认流程能跑通再放开全量。5.2 Boruta 跑了好几个小时不出结果现象BorutaPy 的 fit 一跑就是几个小时进度条看起来永远走不完。原因Boruta 内部要反复训练随机森林每一轮迭代都会在全量特征上训练一次如果特征有几十个、n_estimators 设了 1000、max_iter 又没限制训练轮次就是天文数字。另外前置没做相关性过滤时影子特征数量翻倍更加剧了开销。解决先用相关性过滤把特征压到 30 个以内再跑 Borutan_estimators 从 200 起步即可不要一上来就追求精度。验证时可以设 max_iter10 快速跑一遍看代码没有报错后再加长迭代。boruta BorutaPy( estimatorrf, n_estimators200, max_iter20, # 先用小轮数试跑 random_state42 )5.3 accuracy 很高但恶意样本一个都没抓到现象classification_report 里 accuracy 有 0.95但恶意类别的 recall 是 0.0precision 也是 0.0。原因数据集里恶意样本占比过低比如只有 5%模型只要全部预测为 normal 就能拿到 95% 的 accuracy但它实际上什么都没学。这种情况在 CTU-13 里尤其常见因为 background 流量占了一大块。解决先用 value_counts 看类别分布再决定处理方式。如果恶意样本占比低于 10%优先考虑 class_weightbalanced其次做下采样让正负样本比例到 1:1 到 1:3 之间。评估时只看 F1 和 AUC把 accuracy 直接忽略掉否则会被它误导。5.4 show_data_doh.html 打开是空白页面现象浏览器打开 show_data_doh.html 或 show_data_ctu-13.html页面框架在但图表区域全是空白。原因这类 HTML 通常是 pyecharts 生成的图表渲染依赖外部的 echarts.min.js 脚本。如果生成的 HTML 引用的是 CDN 地址而你电脑在离线环境或 CDN 访问失败图表就加载不出来。解决在生成 HTML 的环境里检查脚本引用方式。pyecharts 的 render 接口默认外链 CDN离线场景可以下载 echarts.min.js 到本地目录修改 HTML 的 script src 指向本地文件如果只是自己看结果直接在 Jupyter Notebook 里使用 render_notebook 输出图表也能正常显示。5.5 复现结果和文档里的数值对不上现象自己跑出来的 F1 和下载包里的 doh_boruta_model_result.csv 对不上差得还挺多。原因sklearn 版本不同会导致树模型的分裂策略细节变化pandas 版本差异会影响填补缺失值的默认行为另外随机种子没固定每次运行结果都会浮动。解决模型里所有随机过程都固定 random_state42train_test_split、BorutaPy、随机森林一个都不能漏用的时候在 requirements 里锁好关键库版本。树模型的可复现性本身就有点玄学跨版本跑数字微差是正常的只要同版本、同种子下能复现就没问题。6. 进阶验证把训练好的模型接到新流量上做推理模型训练完只是第一步毕设里真正体现工程能力的是你把模型导出来、喂一条新的会话特征、得到可解释的判定结果。这一章讲怎么把训练好的模型落地到推理场景并分享一个我常用的验证习惯。6.1 模型导出与单条样本推理用 joblib 把训练好的模型和标签编码器一起存成文件以后做推理时加载。注意特征顺序必须和训练时完全一致否则 predict_proba 的结果就是错的。import joblib import pandas as pd # 训练完成后保存 joblib.dump({model: model, columns: X.columns.tolist(), le: le}, doh_model.pkl) # 推理时加载 pkg joblib.load(doh_model.pkl) clf, cols, le pkg[model], pkg[columns], pkg[le] # 假设 new_flow 是从新捕获流量里提取的会话特征向量 # 必须按训练时的列顺序排列 new_flow pd.DataFrame([new_flow_dict], columnscols) prob_malicious clf.predict_proba(new_flow)[0][1] label le.inverse_transform(clf.predict(new_flow))[0] print(flabel{label}, malicious_prob{prob_malicious:.4f})逻辑说明把列名列表和标签编码器一起存进 pkl是为了在加载模型时强制校验特征顺序避免推理时列名错位这种静默错误。生产环境里建议把概率阈值从默认的 0.5 调高到 0.7 或 0.8因为恶意样本漏报的成本远高于误报提高阈值可以只对高置信样本做拦截。6.2 一个验证习惯看概率分布而不是只看汇总指标我在拆这个项目时养成了一个习惯测试集跑完后不急着看报告而是把真实标签为恶意的样本单独拎出来看它们的 predict_proba 概率分布。如果大部分恶意样本的概率集中在 0.9 以上说明模型决策果断如果大量集中在 0.5 到 0.7 之间说明决策边界附近有一批样本模型对这些样本并不自信这时需要回看特征工程是不是漏了什么。import numpy as np # 取出恶意样本的概率分布 mal_prob y_prob[y_test 1] print(malicious prob quantiles:, np.percentile(mal_prob, [10, 50, 90]))这个检查非常快但能提前发现特征区分度不够的问题比答辩前发现模型不靠谱要体面得多。我从那以后每次跑流量检测项目都强制走一遍这个动作它能让我对模型边界心里有数而不是靠 accuracy 自我感觉良好。希望这个习惯和上面的排查经验能帮到你的毕设。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。