恶意加密流量检测毕业设计实战:从数据集到CNN模型部署

发布时间:2026/10/11 20:21:52
恶意加密流量检测毕业设计实战:从数据集到CNN模型部署
简介本资源为基于深度学习的恶意加密流量检测系统毕业设计完整源码包面向计算机、网络安全相关专业的学生及需要完成课程设计或期末大作业的开发者。项目围绕加密流量特征提取与恶意流量识别展开涵盖DoH与CTU-13等数据集的Boruta特征筛选、相关性分析及模型训练结果可帮助读者理解从数据预处理到模型评估的完整流程。压缩包共217个文件约25.61MB包含4个Python核心脚本、6个CSV特征与结果文件、6个npy数据文件、2个pcap流量样本以及14个HTML可视化页面和若干日志、图片与样式文件代码注释清晰新手也能快速部署运行。目前已有251人学习下载适合作为毕业设计、课程设计的高分参考方案便于对照复现实验并撰写论文。1. 恶意加密流量检测为什么毕业设计选这个方向不容易翻车很多同学做毕业设计时第一反应是找个看起来能跑的题目结果开题后被导师追问你的创新点在哪数据集从哪来模型为什么选这个直接卡壳。恶意加密流量检测这个方向恰好卡在一个舒服的位置它有真实的安全需求企业出口流量里超过八成是加密的传统基于端口和签名的检测手段基本失效又有足够成熟的公开数据集和算法基础不需要你自己去抓几十万条流量。用 Python 加深度学习一台带显卡的笔记本就能跑通全流程。这个系统的核心任务是把网络流量中加密且恶意的那部分识别出来。注意是加密且恶意不是单纯检测恶意流量也不是单纯做加密流量分类。你要处理的是 TLS/SSL 加密后的流量特征比如包长序列、到达时间间隔、上下行字节比而不是去解密内容。适合做这个题的人会 Python 基础语法、装过 numpy 和 pytorch、能看懂混淆矩阵。如果你连 python 安装都要现查教程建议先花两天把环境跑通再回来。2. 从流量到张量数据管线怎么搭才不返工2.1 为什么选 CIC-IDS2017 和 USTC-TFC2016 这两个数据集做恶意加密流量检测数据集决定了你后面所有工作的上限。我一般会推荐两个CIC-IDS2017 覆盖了多种攻击类型DDoS、暴力破解、Web 攻击等流量以 PCAP 格式提供适合做特征工程USTC-TFC2016 是专门针对加密流量的数据集已经切分好会话标注了恶意家族如 Cridex、Geodo、Htbot 等更适合做端到端的深度学习。选 USTC-TFC2016 的理由很直接它把每条流截断成固定长度的字节序列你不需要自己写会话重组逻辑。很多同学在这一步翻车——用 CICFlowMeter 提取特征后发现加密流量的统计特征区分度不够模型准确率卡在 70% 上不去。USTC 的数据格式更干净适合毕业设计的体量。注意下载数据集时确认文件完整性USTC-TFC2016 的 PCAP 文件解压后约 3GB别下到一半断了。2.2 用 Python 把 PCAP 转成模型能吃的张量下面这段代码做三件事读取 PCAP、按五元组切分会话、把每条会话的前 784 字节转成 28x28 的灰度图。为什么是 784 字节因为 28x28 是 LeNet 的标准输入方便你复用经典 CNN 结构也方便可视化排查。import numpy as np from scapy.all import rdpcap, IP, TCP, UDP from sklearn.model_selection import train_test_split def pcap_to_sessions(pcap_path, max_len784): 把 PCAP 按五元组切分成会话每条会话取前 max_len 字节 packets rdpcap(pcap_path) sessions {} for pkt in packets: if IP not in pkt: continue # 五元组源IP、目的IP、源端口、目的端口、协议 if TCP in pkt: key (pkt[IP].src, pkt[IP].dst, pkt[TCP].sport, pkt[TCP].dport, TCP) elif UDP in pkt: key (pkt[IP].src, pkt[IP].dst, pkt[UDP].sport, pkt[UDP].dport, UDP) else: continue raw bytes(pkt) if key not in sessions: sessions[key] bytearray() sessions[key].extend(raw[:max_len - len(sessions[key])]) if len(sessions[key]) max_len: continue # 补齐或截断到固定长度 result [] for key, data in sessions.items(): arr np.frombuffer(bytes(data[:max_len]), dtypenp.uint8) if len(arr) max_len: arr np.pad(arr, (0, max_len - len(arr)), constant) result.append(arr) return np.array(result) # 假设你已经把恶意和良性 PCAP 分开放了 malicious pcap_to_sessions(malicious.pcap) benign pcap_to_sessions(benign.pcap) X np.vstack([malicious, benign]) y np.hstack([np.ones(len(malicious)), np.zeros(len(benign))]) # 归一化到 0-1reshape 成 CNN 输入格式 X X.astype(np.float32) / 255.0 X X.reshape(-1, 28, 28, 1) X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) print(f训练集: {X_train.shape}, 测试集: {X_test.shape})逻辑说明rdpcap读包五元组做会话键bytearray累积字节。参数max_len784是经验值你可以改成 1024 或 1500但要注意显存。random_state42保证每次切分一致方便复现。跑完这段你应该看到类似训练集: (8000, 28, 28, 1)的输出如果数量差太多检查 PCAP 里是不是混了非 IP 包。2.3 特征工程什么时候该用统计特征什么时候直接上原始字节这里有个选型分叉。如果你用 CIC-IDS2017建议走统计特征路线用 CICFlowMeter 提取 80 多维特征包长均值、方差、流持续时间、IAT 等然后喂给 XGBoost 或 MLP。优点是可解释性强答辩时能画出特征重要性图。缺点是加密流量下部分特征区分度下降。如果你用 USTC-TFC2016直接上原始字节序列做 CNN 更省事。我一般会两条路都跑一遍对比 F1 值。统计特征路线在 CIC-IDS2017 上通常能到 95% 以上原始字节路线在 USTC 上能到 98% 左右。毕业设计里选一条主路线另一条作为对比实验写进论文工作量刚好。3. 模型选型与训练CNN、LSTM 还是 Transformer3.1 一维 CNN 和二维 CNN 在流量分类上的差异流量数据本质是一维字节序列但很多论文把它 reshape 成二维图再用 CNN。这两种做法我都试过。一维 CNN 直接对序列做卷积感受野沿字节顺序滑动更符合流量的时序特性。二维 CNN 把 784 字节排成 28x28卷积核在图像上滑动实际上破坏了字节的原始顺序但意外地能捕捉到局部字节模式的共现关系。我的建议毕业设计用一维 CNN 做主模型二维 CNN 作为对比。一维 CNN 的代码更简洁训练更快答辩时解释起来也顺。下面是一个可以直接跑的一维 CNNimport torch import torch.nn as nn class TrafficCNN1D(nn.Module): def __init__(self, input_len784, num_classes2): super().__init__() self.conv1 nn.Sequential( nn.Conv1d(1, 32, kernel_size5, padding2), nn.BatchNorm1d(32), nn.ReLU(), nn.MaxPool1d(2) ) self.conv2 nn.Sequential( nn.Conv1d(32, 64, kernel_size5, padding2), nn.BatchNorm1d(64), nn.ReLU(), nn.MaxPool1d(2) ) self.conv3 nn.Sequential( nn.Conv1d(64, 128, kernel_size3, padding1), nn.BatchNorm1d(128), nn.ReLU(), nn.AdaptiveAvgPool1d(1) ) self.fc nn.Sequential( nn.Linear(128, 64), nn.ReLU(), nn.Dropout(0.5), nn.Linear(64, num_classes) ) def forward(self, x): # x: (batch, 1, 784) x self.conv1(x) x self.conv2(x) x self.conv3(x) x x.squeeze(-1) return self.fc(x) model TrafficCNN1D() print(sum(p.numel() for p in model.parameters()), 个参数)参数说明kernel_size5是流量分类里常用的感受野覆盖 5 个连续字节。BatchNorm1d加速收敛Dropout(0.5)防过拟合。AdaptiveAvgPool1d(1)把变长输出压成固定长度这样你换input_len不用改全连接层。整个模型约 10 万参数笔记本 CPU 也能训。3.2 训练循环里必须监控的三个指标很多同学训练时只看 loss结果模型在测试集上翻车。血泪经验必须同时看准确率、F1 值和混淆矩阵。恶意流量检测是不平衡分类准确率会骗人——如果恶意样本只占 5%模型全预测良性也有 95% 准确率。from sklearn.metrics import f1_score, confusion_matrix import torch.optim as optim device torch.device(cuda if torch.cuda.is_available() else cpu) model model.to(device) optimizer optim.Adam(model.parameters(), lr1e-3) criterion nn.CrossEntropyLoss() # 假设 X_train_tensor, y_train_tensor 已经转成 torch 张量 for epoch in range(20): model.train() optimizer.zero_grad() out model(X_train_tensor) loss criterion(out, y_train_tensor) loss.backward() optimizer.step() model.eval() with torch.no_grad(): pred model(X_test_tensor).argmax(dim1) f1 f1_score(y_test_tensor.cpu(), pred.cpu(), averagebinary) cm confusion_matrix(y_test_tensor.cpu(), pred.cpu()) print(fEpoch {epoch}: loss{loss.item():.4f}, F1{f1:.4f}) print(cm)逻辑说明每轮训练后切到eval()模式关掉 Dropout 和 BatchNorm 的训练行为。f1_score用averagebinary针对二分类。混淆矩阵打印出来重点看假阴性恶意被判成良性的数量这个在安全场景里代价最高。3.3 学习率、批大小和早停的实操参数学习率我一般从 1e-3 开始用 Adam 优化器。如果 loss 震荡降到 1e-4。批大小 64 或 128取决于显存。早停策略连续 5 轮验证集 F1 不提升就停保存最佳模型权重。best_f1 0 patience 5 counter 0 for epoch in range(50): # ... 训练代码 ... if f1 best_f1: best_f1 f1 torch.save(model.state_dict(), best_model.pth) counter 0 else: counter 1 if counter patience: print(f早停于第 {epoch} 轮) break这套参数在 USTC-TFC2016 上通常 15 轮内收敛。如果 30 轮还在 0.7 以下检查数据标签是不是错了或者归一化有没有做。4. 避坑与排查那些让模型精度突然掉点的原因4.1 数据泄漏训练集和测试集混了同一条流现象测试集准确率 99%换一批数据掉到 60%。原因切分时没按会话切同一条流的包被分到训练和测试两边。解决切分前先按五元组聚合确保整条流只出现在一边。用GroupShuffleSplit按流 ID 分组切分。4.2 类别不平衡恶意样本太少导致模型偷懒现象混淆矩阵里恶意类召回率极低模型几乎全预测良性。原因恶意样本占比不到 10%。解决用WeightedRandomSampler给少数类更高采样权重或者在 loss 里加class_weight。我一般用采样器改动最小。from torch.utils.data import WeightedRandomSampler class_counts np.bincount(y_train) weights 1.0 / class_counts samples_weights weights[y_train] sampler WeightedRandomSampler(samples_weights, len(samples_weights))4.3 字节归一化方式选错现象模型完全不收敛loss 在 0.69 附近不动。原因直接把 0-255 的字节喂进网络数值范围太大。解决除以 255 归一化到 0-1。别用 StandardScaler字节数据不是正态分布。4.4 会话截断长度设得太短现象F1 值卡在 0.85 上不去。原因max_len784截掉了太多信息很多恶意流的关键特征在后半段。解决试试 1024 或 1500但注意显存。如果显存不够用AdaptiveMaxPool1d在模型里做降采样。4.5 忘了固定随机种子现象每次跑出来的结果不一样论文里的数字对不上。原因PyTorch、numpy、Python 的随机种子没固定。解决在代码开头加这几行。import random import numpy as np import torch def set_seed(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) torch.backends.cudnn.deterministic True torch.backends.cudnn.benchmark False set_seed(42)5. 把模型变成系统从脚本到可演示的检测流程5.1 用 Flask 包一个最小可用的检测接口毕业设计答辩时光有训练脚本不够最好能演示输入一条流量输出检测结果。用 Flask 写一个 20 行的接口from flask import Flask, request, jsonify import numpy as np import torch app Flask(__name__) model TrafficCNN1D() model.load_state_dict(torch.load(best_model.pth, map_locationcpu)) model.eval() app.route(/detect, methods[POST]) def detect(): data request.json[bytes] # 长度 784 的整数列表 arr np.array(data, dtypenp.float32) / 255.0 tensor torch.from_numpy(arr).reshape(1, 1, -1) with torch.no_grad(): out model(tensor) prob torch.softmax(out, dim1) pred out.argmax(dim1).item() return jsonify({ label: malicious if pred 1 else benign, confidence: prob[0][pred].item() }) if __name__ __main__: app.run(host0.0.0.0, port5000)逻辑说明接收 JSON 格式的字节列表归一化后转张量前向推理返回标签和置信度。参数port5000可以改。测试时用curl发一条请求curl -X POST http://127.0.0.1:5000/detect \ -H Content-Type: application/json \ -d {bytes: [1,2,3,...]}5.2 检测阈值怎么调别用默认的 0.5二分类默认阈值 0.5但在安全场景里漏报的代价远高于误报。我一般把阈值降到 0.3宁可多报几个良性也别放过恶意。调整方法在验证集上画 ROC 曲线找 FPR 最低且 TPR 可接受的点。from sklearn.metrics import roc_curve fpr, tpr, thresholds roc_curve(y_test, probs) # 找 FPR 0.05 时 TPR 最高的阈值 idx np.where(fpr 0.05)[0] best_threshold thresholds[idx[np.argmax(tpr[idx])]] print(f建议阈值: {best_threshold:.4f})5.3 可视化混淆矩阵和 ROC 曲线怎么画进论文论文里放两张图就够了混淆矩阵热力图和 ROC 曲线。用 matplotlib 和 seaborn代码不超过 15 行。import matplotlib.pyplot as plt import seaborn as sns from sklearn.metrics import confusion_matrix, roc_curve, auc cm confusion_matrix(y_test, preds) sns.heatmap(cm, annotTrue, fmtd, cmapBlues) plt.xlabel(Predicted) plt.ylabel(True) plt.title(Confusion Matrix) plt.savefig(confusion_matrix.png, dpi300) fpr, tpr, _ roc_curve(y_test, probs) roc_auc auc(fpr, tpr) plt.figure() plt.plot(fpr, tpr, labelfAUC {roc_auc:.4f}) plt.plot([0, 1], [0, 1], k--) plt.xlabel(False Positive Rate) plt.ylabel(True Positive Rate) plt.legend() plt.savefig(roc_curve.png, dpi300)dpi300保证论文打印清晰。annotTrue在格子里显示数字。这两张图加上 F1 值表格实验部分就完整了。5.4 源码和文档的组织方式最后说一个容易被忽略的点代码目录结构。答辩老师会翻你的源码如果所有文件堆在一个文件夹里印象分直接扣。我一般这样组织project/ ├── data/ # 数据集存放 ├── src/ │ ├── preprocess.py # PCAP 转张量 │ ├── model.py # 模型定义 │ ├── train.py # 训练脚本 │ └── app.py # Flask 接口 ├── models/ # 保存的权重 ├── results/ # 混淆矩阵、ROC 图 ├── requirements.txt # 依赖清单 └── README.md # 运行说明requirements.txt用pip freeze requirements.txt生成。README 里写清楚Python 版本、依赖安装命令、数据放置路径、训练和推理的运行命令。这三样写清楚别人拿到你的源码能跑起来毕业设计的工程完整性就达标了。我自己做这类项目最大的教训是别等到最后一周才跑实验。数据预处理和模型调参各留三天写文档和录演示视频留两天。中间遇到 F1 上不去先检查数据泄漏和类别不平衡这两个坑我踩过不止一次。希望帮到你。本文还有配套的精品资源点击获取