ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于深度学习的恶意加密流量检测毕设源码:DoH与CTU-13特征工程及模型复现

基于深度学习的恶意加密流量检测毕设源码:DoH与CTU-13特征工程及模型复现

简介:本资源面向计算机、网络安全相关专业的本科生与研究生,提供一套基于深度学习的恶意加密流量检测系统完整实现方案,可用于毕业设计、课程大作业或期末项目。压缩包共217个文件,约25.61MB,包含4个Python核心脚本、6个CSV特征数据、6个NPY数据文件、2个pcap流量样本,以及14个HTML可视化页面、8张JPG与7张PNG图表、165个log运行日志,另附md说明文档与css样式文件,覆盖数据预处理、特征筛选、模型训练与结果展示全流程。资源中提供doh与ctu13两套数据集的corr与boruta特征文件及模型结果,便于对比不同特征选择策略对检测效果的影响。目前已有251人学习下载。代码注释详尽,新手也能理解整体架构与关键算法,下载后简单部署即可运行,是快速完成高分毕业设计的实用参考。

1. 恶意加密流量检测毕设:从 CSV 特征到模型结果,这套源码能跑通什么

做安全方向毕业设计的同学,十有八九绕不开一个尴尬:想检测恶意流量,但流量全被 TLS 加密了,payload 根本看不见。这套「基于深度学习的恶意加密流量检测系统」源码,解决的正是这个场景——不碰密文内容,只从流量的统计特征和元数据里把恶意行为揪出来。它自带 DoH 和 CTU-13 两个数据集的 Boruta 与相关性筛选特征文件,还有对应的模型结果 CSV,等于把「特征工程 → 特征选择 → 模型训练 → 结果落盘」这条链路完整铺好了。适合谁?适合毕设周期紧、想快速跑通一个能答辩的深度学习安全项目、又不想从零造数据管道的同学。下面我按实际拆包顺序,把这份资源怎么用、参数怎么调、哪里会翻车讲清楚。

2. 先看懂数据管道:DoH 与 CTU-13 两套特征文件到底怎么用

拿到源码包,第一眼别急着找train.py。这套项目的核心资产其实是那几个 CSV——doh_corr_features.csv、doh_boruta_features.csv、ctu13_corr_features.csv、ctu13_boruta_features.csv,以及对应的*_model_result.csv。它们决定了你后面模型能不能收敛、答辩时能不能讲出「我做了特征选择」这个加分点。所以这一章先把数据管道讲透,再谈模型。

2.1 两个数据集的分工:DoH 走应用层,CTU-13 走网络层

DoH(DNS over HTTPS)数据集针对的是把 DNS 查询塞进 HTTPS 隧道的行为,这类流量的特点是端口是 443、看起来像正常网页访问,但流量的包长分布、请求间隔和普通浏览有细微差别。CTU-13 则是经典的僵尸网络流量数据集,包含多种 botnet 的抓包,覆盖面更广,适合验证模型的泛化能力。

源码里给每个数据集都准备了两种特征版本:

文件含义用途
doh_corr_features.csvDoH 数据集经相关性筛选后的特征快速训练、特征维度低
doh_boruta_features.csvDoH 数据集经 Boruta 算法筛选后的特征特征重要性更可靠
ctu13_corr_features.csvCTU-13 相关性筛选特征同上,换数据集
ctu13_boruta_features.csvCTU-13 Boruta 筛选特征同上
doh_boruta_model_result.csvDoH + Boruta 特征的模型输出结果对比实验、写论文用
ctu13_boruta_model_result.csvCTU-13 + Boruta 特征的模型输出结果同上

为什么同一数据集要出两套特征?因为答辩老师大概率会问「你怎么证明选出来的特征是有用的」。相关性筛选快但粗糙,Boruta 基于随机森林做特征重要性竞争,更严谨但慢。两套都留着,论文里做对比实验,正好凑一章「特征选择方法对比」。

2.2 用 pandas 加载特征并检查标签分布

在动手训练前,先确认特征文件的结构。常见做法是先跑一段探查脚本,看列名、看标签列是否叫label或Label、看正负样本比例。

import pandas as pd # 加载 Boruta 筛选后的 DoH 特征 df = pd.read_csv("doh_boruta_features.csv") # 打印形状和前几列,确认特征维度 print("shape:", df.shape) print("columns:", df.columns.tolist()[:10]) # 查看标签分布,判断是否类别不平衡 label_col = "label" if "label" in df.columns else df.columns[-1] print(df[label_col].value_counts()) # 检查缺失值,加密流量特征里 NaN 很常见 print("missing:", df.isnull().sum().sum())

这段代码的逻辑说明:shape告诉你特征有多少维、样本有多少条,一般毕设数据集在几千到几万条量级;value_counts()看标签是否一边倒,如果恶意样本只占 5%,后面训练必须处理不平衡,否则模型全预测成正常也能有 95% 准确率,答辩时会被追问。isnull().sum()是血泪经验——流量特征里经常有除零产生的 NaN,不处理直接喂给模型会报错或梯度爆炸。

参数上,label_col的自动判断是个兜底写法,因为不同来源的 CSV 标签列名不统一。你拿到手先print(df.columns)看一眼,确认后把列名写死更稳妥。

2.3 特征选择结果怎么读:Boruta 与相关性的差异

*_boruta_features.csv里的列,是 Boruta 算法判定为「比随机阴影特征更重要」的那些特征。Boruta 的原理是给每个真实特征配一个随机打乱的影子特征,跑随机森林看真实特征的重要性是否稳定超过影子特征,超过就保留。它的好处是保留了特征之间的交互信息,不像相关性筛选那样一刀切。

*_corr_features.csv则是按皮尔逊相关系数阈值筛的,通常阈值设在 0.9 以上剔除高度共线特征。它快,但会漏掉那些单独看相关性低、组合起来有用的特征。

实操建议:先用corr版本跑通全流程,确认代码没 bug,再换boruta版本出最终结果。因为 Boruta 特征维度可能更高,训练更慢,调试阶段没必要耗时间。

提示:如果*_model_result.csv里已经有预测值和真实标签,你可以直接拿它算混淆矩阵,不用重新训练就能在论文里放结果图。这是赶进度时的后悔药。

3. 模型训练与结果复现:从特征 CSV 到 model_result 的完整链路

数据看明白了,这一章进入训练环节。源码包没有明确给出训练脚本文件名,但按这类毕设的惯例,核心逻辑集中在一个主脚本里,配合style.css和show_data_*.html做前端展示。你要做的是把特征 CSV 喂进模型,跑出和*_model_result.csv对得上的结果。

3.1 搭建训练脚本:MLP 与 1D-CNN 的选型理由

加密流量特征大多是数值型统计量(包长均值、方差、流持续时间、包间隔等),不是图像也不是序列文本。所以模型选型上,MLP(多层感知机)是最稳的基线,1D-CNN 可以用来捕捉特征之间的局部相关性。常见做法是先用 MLP 跑通,再上 CNN 做对比。

import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from tensorflow.keras import layers, models # 1. 加载 Boruta 特征 df = pd.read_csv("ctu13_boruta_features.csv") X = df.drop(columns=["label"]).values y = df["label"].values # 2. 标准化:流量特征量纲差异大,不标准化模型很难收敛 scaler = StandardScaler() X = scaler.fit_transform(X) # 3. 划分训练集和测试集,stratify 保证类别比例一致 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) # 4. 构建 MLP 模型 model = models.Sequential([ layers.Dense(128, activation="relu", input_shape=(X_train.shape[1],)), layers.Dropout(0.3), # 防止过拟合,流量特征噪声大 layers.Dense(64, activation="relu"), layers.Dropout(0.3), layers.Dense(1, activation="sigmoid") # 二分类输出 ]) model.compile(optimizer="adam", loss="binary_crossentropy", metrics=["accuracy"]) model.summary()

逻辑说明:StandardScaler这步不能省,流量特征里「流持续时间」可能是几千毫秒,而「包长方差」可能是个位数,不标准化的话梯度会被大量纲特征主导。stratify=y是保证训练集和测试集的恶意/正常比例一致,否则小样本类别可能在测试集里消失。Dropout(0.3)是应对过拟合的常规手段,毕设数据量通常不大,不加 Dropout 训练集准确率能到 99%、测试集掉到 80%,这种翻车现场答辩时很难解释。

参数说明:Dense(128)和Dense(64)是经验值,特征维度在 20 到 50 之间时够用;如果 Boruta 特征超过 100 维,第一层可以加到 256。sigmoid用于二分类,如果你要做多分类(比如区分多种 botnet),改成softmax并把损失换成categorical_crossentropy。

3.2 训练、评估与结果落盘

模型搭好后,训练过程要加EarlyStopping,不然容易在验证集上反复震荡。

from tensorflow.keras.callbacks import EarlyStopping # 早停:验证损失 5 轮不下降就停,恢复最佳权重 early_stop = EarlyStopping(monitor="val_loss", patience=5, restore_best_weights=True) history = model.fit( X_train, y_train, validation_split=0.2, # 从训练集再切 20% 做验证 epochs=50, batch_size=64, callbacks=[early_stop], verbose=1 ) # 在测试集上评估 loss, acc = model.evaluate(X_test, y_test, verbose=0) print(f"Test Accuracy: {acc:.4f}") # 保存预测结果,格式对齐 model_result.csv y_pred = (model.predict(X_test) > 0.5).astype(int) result_df = pd.DataFrame({"true_label": y_test, "pred_label": y_pred.flatten()}) result_df.to_csv("my_model_result.csv", index=False)

逻辑说明:validation_split=0.2是从训练集里再切一部分做验证,和测试集完全隔离,这样早停依据的val_loss不会泄露测试集信息。restore_best_weights=True是关键,它保证模型回到验证损失最低的那轮,而不是停在最后一轮。最后把预测结果存成 CSV,列名对齐源码里的*_model_result.csv,方便你直接对比。

参数说明:batch_size=64在几千条样本时比较稳,样本上万可以加到 128。epochs=50配合早停,实际可能十几轮就停了。如果你的机器没有 GPU,这套 MLP 在 CPU 上跑也就几分钟,不用折腾环境。

3.3 用 model_result.csv 做对比实验

源码自带的doh_boruta_model_result.csv和ctu13_boruta_model_result.csv是你写论文的现成素材。你可以加载它们,算准确率、召回率、F1,再和自己跑出来的结果对比。

from sklearn.metrics import classification_report # 加载源码自带的结果文件 ref = pd.read_csv("ctu13_boruta_model_result.csv") print(classification_report(ref["true_label"], ref["pred_label"]))

classification_report会输出每个类别的 precision、recall、f1-score。加密流量检测里,召回率比准确率更值得关注——漏掉一个恶意流量的代价,通常比误报一个正常流量高。如果源码结果的召回率明显偏低,你可以在论文里分析原因,比如特征选择过于激进、样本不平衡没处理,这反而是加分项。

4. 避坑与排查:这套源码跑不起来时先查这几处

毕设源码包有个通病:作者环境能跑,换台机器就报错。下面是我拆这类项目时最常遇到的五个坑,按「现象 → 原因 → 解决」列出来,你对照排查能省不少时间。

4.1 现象:读取 CSV 报UnicodeDecodeError

原因:Windows 下用 Excel 打开过 CSV 再保存,编码可能变成 GBK,而 pandas 默认按 UTF-8 读。

解决:读文件时显式指定编码,pd.read_csv("xxx.csv", encoding="gbk"),或者用encoding="utf-8-sig"处理带 BOM 的文件。如果还不行,用chardet检测一下实际编码。

4.2 现象:模型训练 loss 一直是 nan

原因:特征里有 NaN 或无穷大值,常见于流量特征计算时的除零操作。

解决:训练前加一步清洗,X = np.nan_to_num(X, nan=0.0, posinf=0.0, neginf=0.0),或者用SimpleImputer填充均值。别小看这步,我见过太多人卡在这里以为是模型问题。

4.3 现象:测试集准确率 99%,但换个数据集就崩

原因:过拟合,或者训练集和测试集有数据泄露(比如标准化时用了全量数据)。

解决:标准化只能在训练集上fit,然后transform测试集。正确写法是先划分再fit_transform训练集,测试集只transform。另外检查是否有重复样本同时出现在训练和测试集里。

4.4 现象:show_data_*.html打开是空白

原因:HTML 里引用的style.css路径不对,或者数据是动态加载的需要本地起服务。

解决:确认style.css和 HTML 在同一目录,用浏览器 F12 看 Console 报什么错。如果是 fetch 加载 CSV,需要用python -m http.server起个本地服务,直接双击打开会被跨域策略拦住。

4.5 现象:Boruta 特征文件列数和 corr 文件对不上

原因:两个文件是不同时间生成的,特征集版本不一致。

解决:以boruta文件为准做最终实验,corr文件只用来快速验证代码。如果论文里要对比,确保两次实验用的是同一批原始数据,只是筛选方法不同。

注意:这套源码的*_model_result.csv是结果文件,不是训练输入。别把它当特征文件喂给模型,否则标签列会混进去,准确率虚高到 100%,答辩时一问就露馅。

5. 进阶技巧:把结果文件用出论文价值,顺带验证模型鲁棒性

跑通训练只是及格线,想让这份毕设拿高分,得把自带的*_model_result.csv用出花来。这一章讲两个具体技巧:一是用结果文件做交叉验证式的鲁棒性分析,二是把show_data_*.html改造成答辩演示页面。

先说鲁棒性分析。源码给了 DoH 和 CTU-13 两个数据集的结果,你可以做一个「跨数据集泛化」实验:用 DoH 特征训练,在 CTU-13 上测试,看性能掉多少。掉得少说明特征通用性强,掉得多说明模型过拟合了特定数据集的分布。这个实验不需要重新标注数据,直接用现成 CSV 就能做。

# 跨数据集泛化实验:DoH 训练,CTU-13 测试 doh = pd.read_csv("doh_boruta_features.csv") ctu = pd.read_csv("ctu13_boruta_features.csv") # 取两个数据集的共同特征列,避免列不匹配 common_cols = [c for c in doh.columns if c in ctu.columns and c != "label"] X_train = StandardScaler().fit_transform(doh[common_cols]) y_train = doh["label"] X_test = StandardScaler().fit_transform(ctu[common_cols]) y_test = ctu["label"] # 用之前定义的 model 结构重新训练 model.fit(X_train, y_train, epochs=20, batch_size=64, verbose=0) loss, acc = model.evaluate(X_test, y_test, verbose=0) print(f"Cross-dataset Accuracy: {acc:.4f}")

这段代码的关键在common_cols,两个数据集的特征列名不一定完全一致,取交集才能对齐。StandardScaler这里各做各的,严格来说应该用训练集的 scaler 去 transform 测试集,但跨数据集场景下分布差异大,各自标准化反而更能反映真实泛化能力。跑出来的准确率如果明显低于同数据集测试,论文里就可以写「模型对数据集分布敏感,未来需要引入域适应方法」——这是标准的学术讨论写法,老师爱看。

再说演示页面。show_data_ctu-13.html和show_data_doh.html是现成的数据可视化页面,配合style.css使用。答辩时你可以把预测结果 CSV 转成 JSON 嵌进 HTML,用表格或简单图表展示「哪些流量被判定为恶意」。改法很简单:在 HTML 里加一段 fetch 读取你的my_model_result.csv,用 JavaScript 渲染成表格。如果不想写 JS,直接把 CSV 内容复制进 HTML 的<pre>标签也能凑合演示。

我一般会强制走一遍「换机器复现」流程:把整个包拷到另一台没配过环境的电脑上,从pip install pandas numpy scikit-learn tensorflow开始,一步步跑到出结果。这个过程能暴露 90% 的路径问题和依赖版本问题。从那以后我每次交付毕设代码前,都强制走一遍这个流程,省得答辩前一天还在远程调环境。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进