ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于Python的TCP入侵检测系统:端口扫描与SYN Flood防御实战

基于Python的TCP入侵检测系统:端口扫描与SYN Flood防御实战 简介基于Python构建的TCP入侵检测系统面向毕业设计、课程设计及网络安全方向项目开发。系统围绕TCP请求频率、SYN/FIN/NULL等flag标志位比例、未开放端口请求比例三项核心指标可识别端口扫描、Dos攻击及爬虫行为并联动iptables实现自动防御需配合python-iptables、MySQLdb、scapy库使用。压缩包共6个文件包含5个Python源码文件与1个Markdown说明文档源码按主控、数据嗅探、过滤分析、数据库存储等功能划分方便理解检测流程与二次开发。整体大小仅5KB轻量易读便于扩展。资源已有231人学习适合需要快速搭建入侵检测原型或参考网络防御联动方案的读者。可直接在项目基础上调整检测阈值与防御策略用于实验教学、论文验证或功能延伸同时提供README说明依赖库与启动方式明显降低上手门槛节省从零开发的时间。1. 为什么把入侵检测做在 TCP 层这套系统能防什么在 Linux 服务器上用netstat -an看到成百上千条 SYN_RECV、CPU 被打满、正常请求进不来这是 TCP 层最典型的攻击场景——端口扫描和 SYN Flood 洪泛。这类问题如果只靠人工盯连接状态流量一起来根本看不过来而且响应太慢。基于 Python 实现的 TCP 入侵检测系统就是把抓包、识别、防御串成一个闭环用 Scapy 抓取网络流量识别端口扫描行为与 DoS 攻击特征再联动 iptables 自动封禁攻击源。这套方案原理完整、代码量可控、演示效果好正好覆盖毕设和课程设计对检测 防御两点都要展示的需求。适合对 TCP 协议有一定基础、想动手做一套能跑通并讲清楚的防护原型的开发者。先说结论这套系统的价值不在性能——Python 做不了工业级 IDS——而在于把检测逻辑做透让你能亲手验证每一层行为。2. 系统架构与检测原理三层拆解先想清楚再写代码动手写代码之前我习惯先把系统拆成三层每层只干一件事。这样哪怕写到一半想换检测算法或者想换掉 iptables 改用别的方式下发规则都只需要改动对应层不用推倒重来。2.1 三层架构抓包、分析、联动各管什么系统从上到下分三层数据采集层、流量分析层、联动防御层。数据采集层负责从网卡上抓取原始 TCP 包这一层我直接用 Scapy 的sniff()函数它封装了 libpcap能直接拿到解析好的 IP 层和 TCP 层对象省去手动解析字节流的麻烦。流量分析层是系统核心所有检测逻辑都在这里收到一个包判断它是什么类型SYN、SYN-ACK、ACK、RST然后决定更新哪张表、计数加多少、要不要触发告警。联动防御层最简单也最关键——接到分析层的告警结果后调用系统命令给 iptables 下发规则把攻击源 IP 封掉。分层带来的好处很实际答辩时老师问你系统如何扩展你直接说三层各改各的。抓住包想做更多协议检测在分析层加逻辑就行不想用 iptables换nftables或调用云防火墙 API只动联动层。开始写代码前先把运行环境备好。这套系统必须在 Linux 上跑推荐 Ubuntu/Debian 系需要提前装好 Python 3.8 和 Scapysudo apt update sudo apt install python3 python3-pip tcpdump -y pip3 install scapy装完后先跑一个最小命令验证 Scapy 能正常抓包避免后面排查问题时分不清是抓包问题还是检测逻辑问题sudo python3 -c from scapy.all import sniff; sniff(prnlambda x: x.summary(), count5)如果能打印出 5 行包摘要说明环境和权限都正常。sudo必不可少Scapy 抓原始套接字必须有 root 权限后面运行整个系统时也一律用 sudo。另外tcpdump 虽然不直接用但它的抓包能力依赖底层 libpcap装上能避免一些环境层面的麻烦。2.2 三次握手与攻击指纹检测逻辑都来自这里TCP 入侵检测的核心依据是三次握手的状态变化。一个正常连接的过程是客户端发 SYN服务端回 SYN-ACK客户端再回 ACK连接建立。三次握手里出现的任意偏差都可能对应一种攻击行为。端口扫描和 SYN Flood 在握手过程中的表现有明显区别我把它们的特征整理成下面的对比表这也是后面两章检测逻辑设计的依据行为类型握手特征检测口径正常连接SYN → SYN-ACK → ACK 完整走完连接正常建立从状态表移除端口扫描同一源 IP 短时间内向大量目的端口发 SYN源 IP 触碰的目的端口数在窗口内急剧上升SYN Flood大量 SYN 进来但迟迟没有 ACK 补上握手半连接只有 SYN 状态的数量持续积压端口扫描的本质是扩散扫描器想知道目标主机开放了哪些端口就会对一个 IP 的多个端口发探测包。所以检测方法是统计源 IP 在时间窗口内访问了多少个不同目的端口超过阈值判定为扫描。而 SYN Flood 的本质是不完成握手攻击者发 SYN 后不再回应让服务端维护大量半连接状态耗尽连接表。所以检测方法是看半连接是否在积压而不是简单统计 SYN 包数量。这两个口径后面会反复用到先记住端口扫描看扩散度SYN Flood 看半连接完成率。2.3 核心数据结构状态表、计数器、半连接表检测逻辑要落地需要设计几张核心的数据结构。我这里用 Python 原生的 dict、set、deque 组合不引入额外依赖也方便答辩时讲清楚每一张表的作用。from collections import defaultdict, deque import time # 1. 端口扫描计数器记录每个源IP在窗口内触碰过的目的端口 scan_counter defaultdict(set) # 2. 端口扫描时间轴记录每个源IP的[S权时间, 目的端口]对用于滑动窗口清理 scan_timestamps defaultdict(deque) # 3. 半连接表记录尚未完成三次握手的TCP连接 # key (src_ip, dst_ip, dst_port)value 收到SYN的时间戳 half_open {} # 4. 已封禁IP及其解封时间 ban_until {}为什么要用defaultdict(set)来统计端口而不是直接数 SYN 包个数因为同一个 IP 对同一个端口重发 SYN 是正常行为比如 TCP 重传。如果用包个数当判据一次重传就可能造成误报。set 自带去重天然保证不同目的端口这个口径。但 set 只解决去重解决不了时间窗口扫描器今天碰过某端口明天再扫一次不能因为历史记录就判定为扫描。所以还需要scan_timestamps这个 deque 来记录每次访问的时间点和端口配合滑动窗口不断清掉窗口外的旧记录这样才能准确反映最近 10 秒内的行为。半连接表用一个 dict 就够了key 用三元组(src_ip, dst_ip, dst_port)value 记录首次看到 SYN 的时间。后续收到 ACK 就删掉这条记录定期巡检时把超时的记录清除并计数。这张表在后端检测 SYN Flood 时是主力。数据结构定了下面两章就顺着这个框架把检测逻辑写出来。3. 端口扫描检测实现从 SYN 标志位到滑动窗口判据端口扫描检测是整个系统里最好验证、也最容易讲清楚的部分。核心思路一句话同一个源 IP在时间窗口内触碰了超过阈值数量的目的端口就判定为扫描。下面从读包开始一步步把这个逻辑落地。3.1 先会读一个 SYN 包flags 位与端口扩散写检测代码第一步是从每一行流量里认出这是一个 SYN 包。Scapy 解析后的 TCP 层有flags字段SYN 标志对应的二进制值是 0x02。新手最容易犯的错是写if tcp.flags 0x02这会把 SYN-ACK 包flags 为 0x12漏掉——因为 SYN-ACK 里也带 SYN 标志值的位数不同但语义上同样是一个SYN 响应。正确姿势是用位与操作from scapy.all import sniff, IP, TCP import time from collections import defaultdict, deque SCAN_WINDOW_SEC 10 # 滑动窗口长度单位秒 SCAN_PORT_THRESHOLD 100 # 窗口内触碰的目的端口数阈值 # 端口扫描跟踪表src_ip - 窗口内触碰过的目的端口集合 scan_counter defaultdict(set) # 时间轴src_ip - deque([(时间戳, 目的端口), ...]) scan_timestamps defaultdict(deque) def on_packet(pkt): 处理每一个抓到的TCP包 if not pkt.haslayer(TCP): return # 只处理TCP包跳过其他协议 ip pkt[IP] tcp pkt[TCP] # 判断SYN标志位用位与而不是 否则SYN-ACK包会被漏掉 if tcp.flags 0x02: handle_syn(ip.src, tcp.dport, time.time())pkt.haslayer(TCP)是 Scapy 里非常常用的判断方式因为一个包里可能有多层协议先确认有 TCP 层再往下取字段避免取属性时报错。pkt[IP]拿到 IP 层pkt[TCP]拿到 TCP 层然后取src源 IP、dport目的端口就够用了。3.2 滑动窗口统计代码set 判重、deque 清时间handle_syn函数是端口扫描检测的核心它要做三件事往时间轴里放一条新记录、清掉窗口外的旧记录、检查窗口内端口数是否超阈值。这里最体现细节的是清理逻辑时间轴和计数器必须同步更新否则会出现计数里有这个端口但时间轴里早已过期的不一致状态。def handle_syn(src_ip: str, dst_port: int, now: float) - None: 记录一次SYN访问并判断是否构成端口扫描 # 1. 将本次访问加入时间轴 scan_timestamps[src_ip].append((now, dst_port)) # 2. 滑动窗口清理把超出窗口时间的旧记录清除 while scan_timestamps[src_ip] and now - scan_timestamps[src_ip][0][0] SCAN_WINDOW_SEC: _, old_port scan_timestamps[src_ip].popleft() # 同步从计数器中移除过期端口 if not any(p old_port for _, p in scan_timestamps[src_ip]): scan_counter[src_ip].discard(old_port) # 3. 把当前端口加入计数器 scan_counter[src_ip].add(dst_port) # 4. 判断是否超过阈值 if len(scan_counter[src_ip]) SCAN_PORT_THRESHOLD: print(f[告警] 检测到端口扫描: {src_ip} 在 {SCAN_WINDOW_SEC}s 内触碰了 {len(scan_counter[src_ip])} 个端口) trigger_defense(src_ip, reasonport_scan)先说为什么用 deque 而不是 list。第 2 步里需要从头部不断弹出过期记录list 的pop(0)是 O(n) 操作流量一大性能就崩deque 的popleft()是 O(1)这是标准做法。再说第 2 步里那个any判断的含义。(now, dst_port)是一对记录当窗口滑动后某个端口可能已经不在时间轴里了但它可能还在scan_counter的 set 里。所以要遍历当前时间轴中是否还存在这个端口的记录如果完全不存在了就从计数器里discard掉。这里用discard而不是remove是因为如果端口恰好已经被前面的逻辑移除remove会抛 KeyError而discard不会。trigger_defense是联动防御的入口会在第 4 章实现。现在可以先留一个空函数保证脚本能跑通后面再补 iptables 逻辑def trigger_defense(ip: str, reason: str) - None: 联动iptables封禁攻击源IP具体实现在第4章完成 print(f[防御] 准备封禁 {ip}原因: {reason})判断条件写 SCAN_PORT_THRESHOLD而不是是因为当达到阈值时就应该立即触发而不是等超过才触发少一次防御响应的时间。3.3 阈值怎么定一把抓的 100 改到多少才合适默认参数SCAN_WINDOW_SEC10、SCAN_PORT_THRESHOLD100是我在课程设计环境下的推荐值但这不是死参数。阈值设得太低正常浏览网页就可能误报——一个网页几十个资源请求每个资源来自不同端口很容易碰线设得太高扫描器慢速扫描就能绕过去。参数默认值使用场景调参建议SCAN_WINDOW_SEC10检测窗口期网络越繁忙调越小避免窗口内积压过多正常流量SCAN_PORT_THRESHOLD100端口数判据内网环境建议 60100公网环境建议 150 以上我的调参习惯是先让系统在正常流量下跑 10 分钟把len(scan_counter[src_ip])的最大值打印出来得到一个基线然后把阈值设成基线的 23 倍。比如正常流量下最活跃的 IP 也就碰到 40 个端口那阈值设在 80120 之间比较合理。另外注意一个细节白名单必须提前留好。如果你自己通过 SSH 登录这台服务器SSH 客户端的 IP 也在扫描统计范围内。我做过一次把 SSH 登录的 IP 当成扫描器封掉的蠢事所以现在会在trigger_defense里过滤掉本机和运维白名单地址。4. SYN Flood 检测与 iptables 联动从半连接积压到自动封禁端口扫描检测解决的是谁在摸我的问题SYN Flood 检测解决的是谁在打我的问题。两者的检测逻辑完全不同但联动防御是同一套 iptables 封禁机制。4.1 为什么不能只看 SYN 包数量如果简单统计 SYN 包速率高并发正常业务就能触发误报。比如一台 Web 服务器在秒杀活动时每秒收到上千个正常 SYN 请求SYN 速率瞬间拉满按包量判攻击直接误封一片用户。正确的观察角度是半连接的积压情况。半连接指的是客户端发了 SYN服务端回了 SYN-ACK但迟迟收不到 ACK的连接。正常网络环境里握手都能完成半连接活在表里的时间极短只有 SYN Flood 这种攻击大量 SYN 进来后客户端不再回应服务端的半连接表就会越积越满。所以 SYN Flood 检测的核心指标是半连接数量有没有持续超过阈值。4.2 半连接表的更新与巡检半连接表用第 2 章里定义的half_opendict 来维护。收到 SYN 包时如果这个三元组(src, dst, dst_port)还没在表里就插入一条记录value 记为当前时间收到 ACK 包时说明握手完成把这个三元组从表里删掉收到 RST 或连接拒绝时同样清理掉这条记录。后台巡检线程每秒钟扫一次half_open把所有存在时间超过SYN_FLOOD_TIMEOUT的条目统计出来如果积压数量超过阈值就判定为 SYN Flood 攻击触发防御。import threading SYN_FLOOD_TIMEOUT 5 # 半连接超时时间秒 SYN_FLOOD_THRESHOLD 200 # 积压半连接数阈值 CHECK_INTERVAL_SEC 1 # 后台巡检间隔秒 def on_packet_full(pkt): 完整的数据包处理回调更新半连接表 if not pkt.haslayer(TCP): return ip pkt[IP] tcp pkt[TCP] if tcp.flags 0x02: # 收到SYN加入半连接表 key (ip.src, ip.dst, tcp.dport) if key not in half_open: half_open[key] time.time() elif tcp.flags 0x10: # 收到ACK握手完成删除半连接记录 key (ip.src, ip.dst, tcp.sport) half_open.pop(key, None) def check_syn_flood(): 巡检函数统计超时半连接数量判定是否发生SYN Flood now time.time() expired_count 0 # 统计所有超时未完成的半连接 for key, start_time in list(half_open.items()): if now - start_time SYN_FLOOD_TIMEOUT: expired_count 1 if expired_count SYN_FLOOD_THRESHOLD: # 找出最容易造成积压的目标统计半连接里出现次数最多的目的IP dst_count {} for (src, dst, sport), start_time in list(half_open.items()): if now - start_time SYN_FLOOD_TIMEOUT: dst_count[dst] dst_count.get(dst, 0) 1 top_dst max(dst_count, keydst_count.get) print(f[告警] 检测到SYN Flood攻击: 目标 {top_dst}积压半连接 {expired_count} 个) # 封禁源IP单源攻击场景有效 for key in list(half_open.keys()): src, _, _ key if src not in ban_until: trigger_defense(src, reasonsyn_flood) def defense_loop(): 后台线程主循环周期执行SYN Flood巡检和解封任务 while True: check_syn_flood() # 解封到期的IP now time.time() for ip, expire_at in list(ban_until.items()): if now expire_at: unban_ip(ip) del ban_until[ip] time.sleep(CHECK_INTERVAL_SEC)half_open.pop(key, None)里的None是容错处理——如果收到 ACK 时对应半连接已被清理过pop不会抛 KeyError。巡检时先复制一份list(half_open.items())再遍历防止在遍历过程中修改字典引发运行时错误。关于巡检循环这里用while True time.sleep而不是threading.Timer原因是 Timer 的线程不是后台线程程序要退出时如果还有未到期的 Timer进程会被挂住不退出。而用主循环加 sleep程序退出时直接 kill 整个进程组即可逻辑也好控制。4.3 iptables 联动ban 与自动解封检测到攻击之后联动防御层要做的事情很明确封禁 IP、到期自动解封。封禁用 iptables 的-I参数把规则插入到 INPUT 链最前面。为什么用-I而不是-Aiptables 规则是顺序匹配的如果链尾部已经是 ACCEPT 放行规则追加到末尾的 DROP 规则根本轮不到执行流量全被放了。插到最前面第一条匹配就直接丢弃。import subprocess BAN_SECONDS 300 # 封禁时长单位秒 def ban_ip(ip: str, reason: str) - None: 封禁一个IP默认300秒后自动解封 try: subprocess.run( [iptables, -I, INPUT, -s, ip, -j, DROP], checkTrue, capture_outputTrue, timeout5 ) except subprocess.CalledProcessError as e: # 规则已存在时iptables返回非零属正常情况 print(f[防御] iptables 下发失败: {e.stderr.decode().strip()}) return print(f[防御] 已封禁 {ip}原因: {reason}有效期 {BAN_SECONDS}s) ban_until[ip] time.time() BAN_SECONDS def unban_ip(ip: str) - None: 解封一个IP try: subprocess.run( [iptables, -D, INPUT, -s, ip, -j, DROP], checkTrue, capture_outputTrue, timeout5 ) except subprocess.CalledProcessError: print(f[防御] 解封 {ip} 时规则不存在忽略) else: print(f[防御] {ip} 已解封)subprocess.run是 Python 调系统命令的标准方式。每个参数的含义checkTrue让命令非零退出时抛异常便于确定 iptables 执行失败capture_outputTrue把错误信息存起来方便打印排查timeout5防止 iptables 命令因锁或异常卡死导致 Python 进程阻塞。iptables 在极端情况下会因 xtables 锁等待而挂住timeout 是必要的保险丝。需要注意一个细节同一 IP 重复触发封禁时iptables 第二次加相同规则会报错这属于正常情况在异常处理里打印一行提示就跳过不要当严重错误处理。4.4 封禁策略的选择什么时候封 IP什么时候限速封 IP 这一招不是万能的。SYN Flood 攻击者如果伪造源 IP半连接表里的源地址会非常分散封禁源 IP 不仅封不到真正的攻击机器还可能误伤正常用户。课程设计里如果能讲清楚这个边界是个明显的加分项。我的做法是分两种情况处理半连接里源 IP 比较集中比如同一个源产生了超过阈值 50% 的半连接直接封禁该 IP源 IP 极度分散说明大概率是伪造源地址封 IP 没有意义改用限速兜底。限速的典型做法是用 iptables 的 limit 模块限制单位时间内到达目标端口的新连接数超过部分直接丢弃# 限制对80端口的新建连接速率为每秒100个超过的丢弃 iptables -A INPUT -p tcp --dport 80 -m state --state NEW -m limit --limit 100/second --limit-burst 200 -j ACCEPT iptables -A INPUT -p tcp --dport 80 -m state --state NEW -j DROP这个命令组合的含义要认真说清楚-m state --state NEW只匹配新连接请求limit模块允许每秒放行 100 个新连接突发情况下最多一次性放行 200 个超出部分的 NEW 包全部丢给第二条规则 DROP。这保证了服务端始终能处理一部分正常连接不让半连接表被彻底打爆但这条规则本身并不能清理已积压的半连接——所以必须和半连接巡检配合使用。--limit-burst参数值得单独强调它控制的是突发放行量。如果只配--limit 100/second不配 burst任何流量抖动超出 100/秒就会被丢太脆了。burst 设置为 200 意味着系统允许短时间内的突发流量先通过长期速率才被限制到每秒 100这个思路和流量整形里的令牌桶是一回事。5. 部署避坑与常见问题排查下面这些坑我基本都踩过这套系统逻辑不复杂但从项目能跑通到真正稳定运行中间隔着几个常见的翻车现场。我把它们按现象 → 原因 → 解决整理在下面每一条都是实战里真实遇到过的。5.1 报错 Interface is None 或 no such device现象运行sniff时报Interface is None或者指定网卡后报No such device。原因Scapy 默认抓第一个可用网卡但服务器上可能有 eth0、ens33、docker0 多个网卡默认选择可能落到不对外服务的虚拟网卡上。另外如果不带iface参数Scapy 在某些系统版本上确实会解析不到默认接口。解决先打印出所有网卡再显式指定from scapy.all import get_if_list, sniff print(get_if_list()) # 查看网卡列表找到对外的网卡名比如 eth0 sniff(ifaceeth0, prnon_packet, store0)store0必须加否则 Scapy 会把每个包都存进内存跑半小时内存就被吃光了。这个参数的含义是不保留原始包数据我们实时处理后就不再需要原始包省内存是实打实的。5.2 sniff 在 Windows 上跑不起来现象Windows 下运行 Sniff 报权限错误或者装不上 Scapy 依赖。原因Scapy 抓原始套接字在 Windows 上受很大限制而且需要额外安装 Npcap 驱动才能捕获底层数据包即便装了Windows 的文件系统权限模型和 Linux 完全不同很多网络层操作没法复现。解决我的建议是不要在 Windows 上死磕直接开一台 Ubuntu 虚拟机VMware 或 VirtualBox 都行跑这套系统。如果毕设环境必须演示 Windows 端可以把 Windows 当攻击机Linux 当被保护主机这样分工反而更贴近真实场景。5.3 iptables: Permission denied 或规则下发没有效果现象subprocess.run抛异常错误信息是Permission denied或者命令执行成功但流量照样进来。原因权限问题最常见iptables 需要 root 权限如果 Python 脚本是用普通用户启动的肯定下发失败。规则下发没有效果通常是两个原因一是规则没有插到 INPUT 链最前面被前面的 ACCEPT 规则抢先匹配了二是在容器里跑容器缺少CAP_NET_ADMIN能力iptables 命令虽然有输出但实际没有作用。解决脚本全部用sudo python3 main.py方式运行确认规则插入用-I容器环境启动时显式加权限docker run --cap-addNET_ADMIN ...。验证规则是否生效用iptables -L INPUT -n --line-numbers看规则顺序以及用iptables -L -v查看匹配计数如果计数不增长说明规则压根没被流量命中。5.4 阈值设太低演示时把自己封了现象系统跑起来没几分钟本机 SSH 断连排查发现是trigger_defense把自己的运维 IP 封了。原因SSH 登录、NTP 时间同步、yum/apt 更新这类正常流量也会产生大量到不同端口的连接。特别是运维用的 Windows 机器各类软件后台网络活动很频繁10 秒内访问几十个端口是常有的事阈值设 50 以下必然误报。解决调参前先跑 10 分钟基线把正常网络里每个源 IP 触碰端口的最大值打印出来再基于基线乘 2~3 倍设阈值。同时在trigger_defense里强制过滤白名单# 不管检测结果如何白名单IP永远不封禁 WHITELIST {127.0.0.1, 192.168.1.0/24} # 按需扩充 def trigger_defense(ip, reason): if ip in WHITELIST: print(f[防御] {ip} 在白名单内跳过封禁) return ban_ip(ip, reason)注意白名单里的网段要用ipaddress模块做匹配in只适合精确 IP 判断。5.5 流量一大 Python 就丢包、检测延迟变大现象流量稍微起来一点检测日志出现断层明显是漏包了严重时整个系统卡住。原因sniff(prnon_packet)的回调函数是在接收线程里同步执行的。如果on_packet里做的事太多——比如用了time.sleep、执行了子进程调用 iptables、做了复杂的打印——接收线程就被拖住了下一批包没地方放直接被内核丢弃。解决把回调做轻抓包回调里只更新内存里的数据结构所有联动防御动作放到另一个线程消费队列。简单做法是把ban_ip的调用丢到一个队列里后台专门有个线程做 iptables 下发import queue, threading defense_queue queue.Queue() def trigger_defense(ip, reason): defense_queue.put((ip, reason)) def worker(): while True: ip, reason defense_queue.get() ban_ip(ip, reason) threading.Thread(targetworker, daemonTrue).start()这样回调函数里只做一次queue.put时间是微秒级的接收线程永远不会阻塞。队列消费线程慢慢处理 iptables 下发即使 iptables 卡顿几秒也不影响抓包继续。课程设计演示时流量不会太大但把这种结构写进代码里答辩能讲的东西又多了一层。6. 攻击模拟与效果验证用 nmap 和 hping3 把系统打一遍系统写完不是终点你得证明它真的能检测、能防御这个环节在毕设验收里几乎是必问的。我建议搭一个最小实验环境一台攻击机、一台被保护主机跑这套 IDS 系统网络互通即可。如果只有两台虚拟机也能做把被保护主机上同时跑一个测试用的 Web 服务当作攻击目标就行。角色分配建议攻击机装 Kali Linux自带 nmap 和 hping3省去额外安装被保护主机装 Ubuntu 服务器版跑 Python 检测脚本和一个简单的python3 -m http.server 80作为靶子服务。先启动检测系统然后从攻击机发起两类攻击# 1. 端口扫描SYN扫描目标主机前1000个端口 nmap -sS 被保护主机IP # 2. SYN Flood攻击对目标主机的80端口发起洪泛 hping3 -S -p 80 --flood 被保护主机IP验证效果分两步走。第一步看检测系统的终端输出是否打印出[告警] 检测到端口扫描和[告警] 检测到SYN Flood攻击的日志。第二步在 Linux 上执行iptables -L INPUT -n --line-numbers确认攻击机的 IP 出现在 DROP 规则里。更直观的验证方式封禁后从攻击机再执行一次nmap -sS会看到目标主机所有端口都变成 filtered说明流量在中间就被丢弃了。验证动作预期结果nmap 扫描触发封禁后再次扫描所有端口显示 filtered 或无响应iptables -L INPUT -n存在攻击机 IP 的 DROP 规则攻击机停止攻击后等 BAN_SECONDS 秒封禁规则自动消失白名单 IP 发起同样的扫描不触发封禁日志提示已在白名单最后说一下参数调优和系统的边界。最深的感受是调阈值不能拍脑袋我改过三版才把误报压下去最有效的办法永远是先收集正常流量基线再设定阈值。这套系统做完你最好也清楚它的局限Python 加 Scapy 的架构决定了它扛不住大流量真实环境里做检测用的是 Snort、Suricata 这类 C 语言实现的引擎以及硬件加速方案。但用来理解 TCP 攻击原理、实践联动防御流程、完成一个完整的毕设课题这套方案足够扎实。希望帮到你。本文还有配套的精品资源点击获取
返回列表