资讯详情

深度学习检测网络扫描流量:从特征工程到模型实战

📅 2026/10/23 5:27:45 | 华诺云谱 👁 阅读
深度学习检测网络扫描流量:从特征工程到模型实战
看到这个题目我第一反应是终于有人愿意把目光放在“攻击发生前”的阶段了。网络扫描是绝大多数攻击的起点端口扫描、存活探测、服务版本识别这些动作看起来不起眼但一旦被忽略后面就是漏洞利用、权限维持、横向移动一条龙。而用深度学习来做扫描流量检测本质上是在和安全分析师的“经验直觉”比赛目标是从海量流量里把这种试探性行为捞出来而且要比规则引擎更灵活、更能适应变体。这篇内容既适合正在做信息安全方向毕业设计的同学也适合刚接触流量安全、想找一个能落地的研究切入点的从业者。我会把从选题拆解、数据准备、特征工程、模型选型到踩坑排查的整个闭环都过一遍所有环节都是我自己带项目时真实反复验证过的思路。1. 项目整体设计与思路拆解1.1 为什么用深度学习而不是只靠规则特征先说一个很现实的问题用传统方法能不能检测网络扫描能。经典做法是Snort规则、NetFlow阈值统计或者设定“短时间内连接大量不同目的端口”这类启发式规则。这些方案部署简单、解释性强真实场景里到现在也还在用尤其在边界防火墙上几乎每个安全设备都会内置类似“SYN Flood”“端口扫描”的检测项。但传统方案有明显的天花板。第一是漏报攻击者会刻意放慢扫描速度、随机化源端口、伪造源IP把行为稀释到和正常流量差不多第二是误报内网运维人员做资产盘点、分布式系统做健康检查也会一瞬间产生大量连接阈值设高了漏真实扫描设低了每天告警堆积如山。第三是难扩展每换一种扫描工具就要重新提炼特征规则规则库里五千条告警真正有用的没几条。深度学习在这件事上的优势不在于“魔法”而在于它能把“特征组合”这件事自动完成。模型看到的输入是流量导出的统计特征输出是“正常”或“扫描”的概率。中间那些复杂的非线性关系——比如“低包长均值 高目的端口熵 短连接时长”的组合——不需要人一条条写出来网络自己会学会。对一个毕设来说这个切入点既不至于庞大到做不完又能覆盖真实安全产品中的核心问题性价比很高。1.2 检测目标要界定清楚检测“阶段”不是检测“结果”很多人一上来就把题目想大想做一个“检测所有恶意流量”的系统。这会让毕设失控因为恶意流量的种类太多漏洞利用、C2通信、数据回传每个方向的数据分布完全不同短期内根本训不出一个可靠的统一模型。合理的做法是聚焦“扫描阶段”。网络攻击的生命周期大概可以分成侦察、武器化、投递、利用、安装、指挥控制、目标行动这几个阶段。扫描行为发生在侦察期它的显著特点是源主机在短时间内与大量目标主机或大量端口建立连接但成功完成完整交互的比例很低。这个定义足够清晰也容易从流量里提取判别性特征。把任务定位成“识别扫描行为”后实际是在做一个二分类问题给定一段流量对应的会话或时间窗口判断是否属于扫描活动。这个界定带来的另一个好处是评价指标可以做得非常具体。扫描检测最怕的不是把扫描漏成正常因为攻击者可能还会后续动作最怕的是把正常运维流量误报成扫描导致安全团队每天被无效告警折磨。所以模型评估时要同时盯住召回率和误报率两个指标这一点后面会展开讲。1.3 技术方案选型从三个候选中聚焦我见过不少同学上来就堆模型随机森林、SVM、CNN、LSTM、Transformer全试一遍最后写在论文里的实验对比是“我的模型比所有基线好”但说不出好在哪这是典型的方案选型缺失。在扫描流量检测这个场景里适合对比的模型其实只有三类。第一类是传统机器学习基线随机森林和梯度提升树。它们的特点是对表格型统计特征非常友好训练快、可解释性尚可是“必须有的下限”。第二类是时序模型LSTM或GRU。扫描行为本身有时间序列特性前一个连接的目的端口可能决定了后一个连接的地址变化所以把流量按时间组织成序列后时序模型天然契合。第三类是基于注意力的结构简单自注意力或小型Transformer。它在捕捉长距离依赖上有优势但训练成本高、数据需求大在毕设这种中等规模数据集下很容易过拟合。我最终选择的组合是特征层面用流统计特征 时间窗口聚合模型层面用一维CNN叠加LSTM的混合结构。CNN负责提取局部时序模式比如密集连接突然出现的规律LSTM负责捕捉更长程的依赖比如扫描者抬高扫描速度或切换网段时的变化。这个结构比纯LSTM收敛快比纯CNN更能利用顺序信息参数量也在一个GPU可以接受的范围内。基线则选随机森林形成“传统方法 vs 深度方法”的完整对比叙事。2. 核心细节解析与实操要点2.1 数据集怎么准备公开数据与自建模拟缺一不可这是毕设里最容易被低估的环节。很多人直接下载公开的入侵检测数据集然后不做任何清洗就开始训练最终发现结果很好但不知道好在哪里或者结果很差但不知道差在哪里。公开数据集的好处是省时间、有标签、有评测基准。但扫描检测有个现实问题很多公开数据集里的“扫描”是单独文件里的全流量注入标签粒度粗糙而且真实网络环境下扫描往往和正常业务流量交织在一起。直接用这类数据训练出来的模型拿到真实环境里会“水土不服”。我的做法是双轨并行先用公开数据集做预训练验证再在本地实验环境里自建扫描样本。自建环境不复杂准备三台虚拟化主机一台作为攻击源一台作为目标一台作为正常业务客户端。攻击侧用常见的端口扫描工具分别跑全连接扫描、半连接扫描、UDP扫描和慢速扫描正常侧模拟网页访问、DNS查询、文件传输、远程登录这类业务流量。用交换机的端口镜像或直接在主机上抓包把流量按时间戳混合起来。这个过程最大的收获是能直接看到样本分布。攻击工具的默认参数会让扫描行为非常明显比如一秒钟发几千个SYN包。但真实攻击者会用慢速模式比如每秒几个连接、持续半小时。所以自建样本时一定要改变速率、随机化源端口、切换扫描目标段让数据贴近真实对抗场景。此外切分数据集时要保证同一时段的正常流量和扫描流量都在训练集或测试集中不能随机切分后互相污染。2.2 特征工程把网络包变成能训练的数字深度学习虽然号称可以自动提取特征但输入绝对是微笑绿绿的关键。网络流量本身是二进制报文模型读不懂必须先转成结构化的统计特征。我在项目里定义了两层特征。第一层是流级别的统计特征针对每个五元组源IP、目的IP、源端口、目的端口、传输层协议建立的会话提取变量数据包总数、总字节数、持续时间、SYN包占比、ACK包占比、窗口大小平均值、TTL平均值等。第二层是滑动窗口聚合特征为了解决单一会话“看不出扫描意图”的问题按时间窗口把同一个源IP发起的连续会话合并计算窗口内连接的IP数量、端口数量、端口熵、时间间隔均值与方差、失败连接比例等。这两个层面组合起来模型既能看到“单个交互是否异常”也能看到“整体行为是否像扫描”。一个简单例子UDP扫描的报文通常很小每个包只有几十字节目标端口集中在高范围且几乎没有返回报文。单看一个UDP会话它就是“一个源端口访问一个目的端口”完全正常。但滑动窗口聚合后“几十个不同目的端口 极短时间间隔 高端口熵”的组合立刻暴露了扫描行为。这就是为什么聚合特征不能省。特征处理还有一个容易踩的坑归一化要在训练集上拟合再应用到验证集和测试集。直接对全量数据做MinMaxScaler会引入未来数据信息看起来效果很好但部署后一上线就崩因为真实流量里随时会出现超过训练范围的特征值。下面是特征构建阶段的简化代码示意import pandas as pd import numpy as np from sklearn.preprocessing import MinMaxScaler # 假设df已按五元组分流包含每一流的统计字段 feat_cols [ packet_count, byte_count, duration_syn_ratio, ack_ratio, window_size_avg, dst_port_entropy, failed_ratio ] X df[feat_cols].copy() X X.fillna(0) scaler MinMaxScaler() # 注意只对训练集fit验证/测试集用同一个scaler做transform X_train scaler.fit_transform(X_train[feat_cols]) X_test scaler.transform(X_test[feat_cols])2.3 模型构建与损失函数一个不算复杂的混合结构模型结构上我推荐一维CNN加LSTM的混合网络而不是直接上Transformer。原因前面说过了数据集规模决定了复杂模型容易过拟合。这里给出一个可运行的参考结构。输入形状是[batch_size, window_len, feature_dim]其中window_len是滑动窗口数量feature_dim是每个窗口的特征数量。先经过一维卷积层提取局部变化模式再用最大池化降低时间维度送入双向LSTM最后经过全连接层输出二分类概率。import torch import torch.nn as nn class ScanFlowNet(nn.Module): def __init__(self, input_dim, hidden_dim128, num_layers2, num_classes2): super().__init__() self.conv1 nn.Sequential( nn.Conv1d(input_dim, 64, kernel_size3, padding1), nn.BatchNorm1d(64), nn.ReLU(), nn.MaxPool1d(kernel_size2, stride2) ) self.lstm nn.LSTM( input_size64, hidden_sizehidden_dim, num_layersnum_layers, batch_firstTrue, bidirectionalTrue ) self.classifier nn.Sequential( nn.Dropout(0.3), nn.Linear(hidden_dim * 2, 64), nn.ReLU(), nn.Dropout(0.2), nn.Linear(64, num_classes) ) def forward(self, x): # x: [batch, window_len, feature_dim] x x.permute(0, 2, 1) # [batch, feature_dim, window_len] x self.conv1(x) x x.permute(0, 2, 1) # [batch, new_len, channel] lstm_out, _ self.lstm(x) # 取最后一个时间步或做全局平均池化 out self.classifier(lstm_out[:, -1, :]) return out损失函数上我建议用带类别权重的交叉熵或者Focal Loss。扫描样本在真实流量里的占比通常很低如果直接用普通交叉熵模型很容易“学懒”把所有样本都预测成正常类因为这样损失也已经很低了。Focal Loss会降低易分类样本的损失权重让模型把注意力集中在少数难分类的扫描样本上这个设计能直接改善召回率。训练参数我常用的配置是批量大小64初始学习率1e-3优化器AdamW训练50轮以内学习率使用余弦退火配合早停策略。早停的patience设10轮监控验证集的F1分数而不是loss。因为F1分数直接反映分类质量loss降了不一定代表F1在升。3. 实操过程与核心环节实现3.1 从pcap到特征表的处理链路整个项目的核心工程链路是流量采集 - 协议解析 - 会话流统计 - 时间窗口聚合 - 特征归一化 - 模型训练 - 阈值调优 - 效果评估。第一步是流量采集。在实验环境里我通常用tcpdump在网关侧监听命令参数是tcpdump -i eth0 -nn -s 0 -w capture.pcap。-nn告诉tcpdump不做域名和端口到服务名的反向解析避免DNS查询影响抓包性能-s 0表示抓取完整报文防止截断导致解析失败。第二步是协议解析。这里可以用tshark一次性把关键字段导出来。需要导出的字段包括frame.time_epoch、ip.src、ip.dst、ip.proto、tcp.srcport、tcp.dstport、udp.srcport、udp.dstport、tcp.flags.syn、tcp.flags.ack、frame.len。把这些字段通过tshark导出成CSV后后续处理全部交给Pandas。第三步是五元组聚合。数据清洗后按(源IP、目的IP、源端口、目的端口、协议)生成会话唯一ID然后统计每个会话内的包数、字节数、持续时间、SYN/ACK标志位占比。这一步计算量不大但要注意会话超时时间的设置。通常超过60秒没有新报文的会话就认为已经结束避免一个长期连接占用太多内存。第四步是滑动窗口聚合。按源IP分组把同一源IP在10秒窗口内发起的会话合并成一条记录。每条记录里统计访问的不同目标IP数、不同目的端口数、端口熵、平均连接间隔、连接失败比例。注意窗口可以重叠也可以用步长滑动比如窗口10秒、步长5秒检测延迟和计算成本之间取平衡。3.2 模型训练与阈值选择后处理比想象中重要模型训练本身的流程比较标准这里说几个容易忽略的点。数据集划分建议按时间顺序依次切成训练集、验证集、测试集按比例大致是70%、15%、15%。不能用随机划分因为同一批扫描工具的流量在时间上高度连续随机划分会把“同一个扫描行为的一部分”分到训练集、另一部分分到测试集评估结果虚高。训练完成后输出的是每个样本属于“扫描”类的概率范围在0到1之间。默认阈值是0.5但实际使用中这个值往往不是最优的。正确做法是在验证集上遍历阈值比如从0.1到0.9步长0.05选择F1分数最高的点作为最终阈值。我测试下来适合的阈值经常在0.6到0.75之间取决于扫描样本在数据中的比例。阈值确定后还需要画PR曲线和ROC曲线。对不平衡数据来说PR曲线比ROC曲线更有参考价值。ROC曲线在负样本远多于正样本时容易显得“过于乐观”PR曲线则更真实地反映“预测为正的样本里有多少真正的扫描”。3.3 关键实验记录一份可复现的实验配置我把一套比较稳定的实验配置整理成表格方便直接抄作业。环节具体配置说明窗口大小10秒能覆盖慢速扫描的短期模式步长5秒保证检测延迟不超过5秒特征数量18个流统计8个 窗口聚合10个输入长度12个窗口相当于观察最近60秒行为模型结构Conv1d(64) BiLSTM(128)平衡效果与训练速度损失函数Focal Loss (gamma1.5)缓解正负样本不平衡优化器AdamWlr1e-3配合余弦退火训练轮数最大50轮 早停patience10类别权重正样本权重3进一步偏向召回这套配置在一个中等规模的数据集上验证集F1能做到0.93左右测试集F1大约0.91误报率控制在5%以内。这个结果单独看不算惊艳但它的意义在于稳定调整数据比例、更换扫描工具后指标波动不会很大。4. 常见问题与排查技巧实录4.1 正常流量误报偏高怎么往下压这是被问得最多的问题。模型在测试集上表现不错一到验证集或真实环境里每天正常流量误报成扫描的占比很高。第一件事先看误报样本长什么样。把预测错误的正常样本单独导出按特征聚类。我遇到的情况是“短时间内大量访问不同IP的DNS请求”和“Windows更新服务批量下载文件”这两类最容易被误判成扫描。前者是因为单源IP访问大量目标后者是因为建立了大量短连接。这说明纯粹的统计特征还不足以区分“主动探测”和“合法批量请求”。解决方案是在特征里增加“双向交互完整性”特征比如返回SYN-ACK的比例、应用层协议类型、TLS握手成功率。扫描者通常不关心响应内容而是快速消耗连接正常业务则会在连接建立后继续交互应用层流量占比明显更高。增加这些特征后误报样本大幅下降。另一个有效手段是调整损失函数里的负样本权重。Focal Loss的alpha参数控制正负样本的整体权重把负样本正常流量的权重适当调高模型就不那么容易因为“扫描特征明显”而把所有可疑流量都判为正。4.2 类别不平衡导致模型“全猜正常”扫描样本只占总样本的10%不到训练时模型会找到最简单的捷径把所有样本都预测成正常类准确率还能有90%以上。这也是为什么不能用准确率当主要指标。处理方法分三层。第一层是数据层面对扫描样本做SMOTE过采样或者在训练时使用WeightedRandomSampler让每个batch里正常和扫描样本比例接近1:1。第二层是损失函数层面用Focal Loss或者普通交叉熵加pos_weight。第三层是评估层面坚持用F1、召回率、PR曲线作为主要指标。我在项目中实测过只用普通交叉熵时召回率大概0.65换成Focal Loss后召回率提升到0.87代价是误报率略有上升通过阈值调整又降回去了。这个提升幅度很值得。4.3 训练集效果很好测试集崩盘是哪里出了问题大概率是数据泄漏或者过拟合。数据泄漏最典型的来源是全局归一化前看到测试集分布。另一个隐藏泄漏点在特征聚合如果用整条pcap的统计信息生成窗口特征相当于用“未来数据”帮助判断当前窗口测试时当然表现好。过拟合问题则更容易判断训练loss持续下降验证loss先降后升。这时候首先检查模型参数量是否过大把LSTM隐藏层维度从128降到64或者把Dropout从0.1提高到0.3其次检查数据量如果只有几千条样本深度学习模型很难学出泛化模式这时宁可把两个不同时间段的数据合并也不要强行找一个还缺数据的模型。4.4 常见坑点速查表坑点现象解决方案时间戳精度不够窗口聚合后特征错位统一使用epoch毫秒时间戳会话流未设置空闲超时长期连接把窗口撑爆超过60秒空闲自动切分源端口随机化被忽略模型把随机端口当特征聚合端口数时排除源端口低估加密流量占比TLS扫描漏报增加TLS握手特征阈值固定为0.5召回率不达标在验证集上搜索最优阈值只用公开数据集真实环境泛化差自建模拟流量混入训练5. 关于整个项目的一点经验体会做这类题目最容易被卡住的不是模型而是数据链路。很多同学在下载完数据集后就直接开始跑模型最后一改数据格式就全部推翻重来。我自己带项目时一定会让学生先把“pcap到特征表”的脚本写好并且保证同一套脚本能处理自己采集的数据再去碰模型。这条链路打通了后面调模型都是小问题。还有一个很实用的建议整个项目里至少要准备两套数据一套是公开数据用于跑通流程另一套是自建数据用于验证真实场景下的效果。两套数据的特征处理必须保持完全一致只是来源不同。最后论文里对比两套数据上不同模型的效果差异这会比单纯调参有意义得多。另外如果真的在校验集上出现了F1很高但随机抽几个误报样本发现明显不合理的场景不要急着加复杂模型。先用特征分析工具把误报样本的特征分布画出来找到它们和正常样本最接近的那几个维度很多时候只要加一个统计特征就能解决不需要换模型。这也是深度学习和传统经验结合最舒服的地方。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑