
简介这是一套基于Python实现的TCP入侵检测系统源码面向网络安全初学者、计算机专业学生及需要完成毕业设计或期末大作业的开发者。项目聚焦端口扫描与DoS攻击的实时检测并联动iptables实现自动防御帮助读者理解入侵检测的基本原理与落地方式。压缩包共6个文件包含5个py源码文件与1个md说明文档整体约5KB代码结构清晰、注释完整新手也能快速看懂。核心模块涵盖数据包嗅探、流量过滤、攻击分析与数据库记录等环节便于按功能拆解学习。目前已有111人学习下载可作为网络安全课程设计、毕设或大作业的参考方案也能用于搭建轻量级实验环境验证检测规则与防御策略的有效性具有较高的实践参考价值。1. 从一次内网告警说起这套 Python TCP 入侵检测系统到底能干什么去年帮朋友看一台暴露在公网的测试机日志里出现大量半开连接netstat刷屏但机器上没装任何像样的防护。当时我第一反应不是去装商业 IDS而是想找一个能读懂、能改、能跟 iptables 联动的轻量方案。后来翻到这套基于 Python 的 TCP 入侵检测系统它做的事情很直接抓包、识别端口扫描和 DoS 特征、命中规则后调用 iptables 封禁来源 IP。整个链路从Data_Sniff.py抓包到Analysis.py分析再到Flitter.py过滤和联动逻辑清晰代码注释也够新手能顺着读下来。它适合两类人一类是拿它做毕业设计或期末大作业需要一套功能完整、能跑起来、导师看得懂的项目另一类是想在内网或测试环境里快速搭一个可定制的检测防御原型不想被重型框架绑住。这套源码不是玩具端口扫描和 DoS 的检测逻辑是实打实写的iptables 联动也不是摆设部署完就能看到封禁效果。2. 拆开源码看结构六个文件如何串起抓包、分析与防御2.1 文件分工与调用关系拿到压缩包解压后是这几个文件Main.py、Analysis.py、Flitter.py、Data_Sniff.py、Database.py、README.md。别急着运行先把调用关系理清楚不然出了问题都不知道去哪找。Main.py是入口负责初始化配置、启动抓包线程、拉起分析循环。Data_Sniff.py封装了原始套接字抓包拿到 TCP 包后交给Analysis.py。Analysis.py是核心里面做了两件事一是统计单位时间内同一源 IP 的 SYN 包数量判断端口扫描二是统计同一源 IP 在短时间内的连接频率判断 DoS 倾向。命中之后Flitter.py负责执行 iptables 规则把来源 IP 加进黑名单。Database.py用 SQLite 记录告警和封禁历史方便回溯。常见做法是抓包用 scapy但这份源码用的是原始套接字好处是依赖少坏处是权限要求高后面避坑章节会细说。2.2 关键参数在哪里改在Main.py里通常会有几个全局配置我一般会先翻这几个值# Main.py 中常见的配置段以实际源码为准 SNIFF_INTERFACE eth0 # 抓包网卡虚拟机里可能是 ens33 SYN_THRESHOLD 100 # 单位时间内 SYN 包阈值超过判定为端口扫描 DOS_THRESHOLD 200 # 单位时间内连接数阈值超过判定为 DoS TIME_WINDOW 10 # 统计时间窗口单位秒 BLOCK_DURATION 3600 # iptables 封禁时长单位秒SNIFF_INTERFACE必须改成你实际在用的网卡名用ip addr或ifconfig确认。SYN_THRESHOLD和DOS_THRESHOLD是最容易翻车的地方设太小会误封正常业务设太大又检测不到攻击。我一般先在测试环境用hping3打一轮观察日志里统计值的变化再定阈值。TIME_WINDOW决定了统计的灵敏度10 秒是个折中值太短会抖动太长会漏判。2.3 数据库表结构与告警记录Database.py里一般会建两张表一张记告警一张记封禁。字段大致是字段名类型说明idINTEGER自增主键src_ipTEXT攻击来源 IPattack_typeTEXTscan 或 doscountINTEGER触发时的统计值timestampDATETIME告警时间blockedINTEGER是否已封禁0/1这张表的价值在于事后复盘。有一次我误封了一台监控服务器就是靠查这张表发现它的连接频率刚好卡在阈值边缘后来把DOS_THRESHOLD往上调了 50 才稳定。所以别嫌数据库麻烦它是你排查误报的唯一后悔药。3. 部署与运行从零把检测和 iptables 联动跑通3.1 环境准备与依赖安装这套源码依赖不多但有几个是必须的。Python 版本建议 3.8 以上和热搜里常提的 python 3.8 一致兼容性最稳。原始套接字抓包需要 root 权限所以后面所有命令都要加sudo。# 更新包列表并安装 Python3 和 pip sudo apt update sudo apt install -y python3 python3-pip # 安装源码可能用到的依赖以实际 import 为准 pip3 install scapy sqlite3如果Analysis.py里用了scapy做包解析那scapy必须装。sqlite3是 Python 内置的通常不用单独装但有些精简系统会缺装一下不亏。装完用python3 -c import scapy验证没报错就说明环境通了。3.2 网卡配置与抓包启动先确认网卡名这一步不能猜# 查看所有网卡及状态 ip addr show输出里找到有inet地址且状态UP的那个比如ens33或eth0。把它填进Main.py的SNIFF_INTERFACE。然后启动主程序# 必须以 root 运行否则原始套接字无法创建 sudo python3 Main.py启动后如果看到类似Sniffing on ens33...的输出说明抓包线程起来了。这时候在另一台机器上用nmap扫一下这台机的端口观察控制台有没有告警打印。如果没有先别怀疑代码去检查网卡名和防火墙是否把包拦在了抓包之前。3.3 iptables 联动逻辑与验证Flitter.py里执行的核心命令通常是这样的# 封禁来源 IP拒绝所有入站 sudo iptables -I INPUT -s 攻击IP -j DROP # 查看当前封禁列表 sudo iptables -L INPUT -n --line-numbers-I INPUT表示插入到规则链顶部优先级最高。-s指定来源 IP-j DROP直接丢弃。验证的时候用被判定为攻击的那台机器ping目标机如果超时说明封禁生效。注意这套逻辑默认是永久封禁源码里如果有BLOCK_DURATION通常会配合一个定时解封的线程没有的话就得手动iptables -D删除规则。我一般会加一条定时任务每小时清理一次过期规则避免黑名单无限膨胀。4. 检测逻辑深挖端口扫描和 DoS 是怎么被认出来的4.1 端口扫描的 SYN 统计法端口扫描最典型的特征是短时间内大量 SYN 包发往不同端口。Analysis.py里的做法一般是维护一个字典key 是源 IPvalue 是该 IP 在当前时间窗口内的 SYN 包计数。每收到一个 SYN 包计数加一同时检查是否超过SYN_THRESHOLD。超过就判定为扫描。# Analysis.py 中端口扫描检测的简化逻辑 from collections import defaultdict import time syn_count defaultdict(int) # 源 IP - SYN 计数 last_reset time.time() def check_scan(src_ip): global last_reset now time.time() # 每过 TIME_WINDOW 秒清零一次计数 if now - last_reset TIME_WINDOW: syn_count.clear() last_reset now syn_count[src_ip] 1 if syn_count[src_ip] SYN_THRESHOLD: return True # 判定为端口扫描 return False这段逻辑的关键在TIME_WINDOW和SYN_THRESHOLD的配合。窗口太短正常用户的并发连接也可能触发窗口太长扫描器慢慢扫就检测不到。我一般会把窗口设成 10 秒阈值设成 100这个组合在测试环境里对nmap -sS的检出率比较高误报也还能接受。4.2 DoS 攻击的连接频率判定DoS 的判定比端口扫描稍微复杂一点因为它不只看 SYN还要看整体连接频率。常见做法是统计同一源 IP 在时间窗口内发起的连接总数包括 SYN、ACK 和已完成的三次握手。如果这个总数超过DOS_THRESHOLD就判定为 DoS 倾向。# DoS 检测的简化逻辑统计所有 TCP 包 conn_count defaultdict(int) def check_dos(src_ip, tcp_flags): # 只统计新建连接相关的包 if S in tcp_flags or A in tcp_flags: conn_count[src_ip] 1 if conn_count[src_ip] DOS_THRESHOLD: return True return False这里有个细节tcp_flags的判断要准只统计新建连接相关的包不然正常的数据传输也会被算进去导致误封。我见过有人把所有的 TCP 包都计数结果一台文件服务器传个大文件就被封了血泪经验。所以DOS_THRESHOLD一定要结合业务流量来调没有万能值。4.3 阈值调优的实操方法调阈值不能拍脑袋我一般分三步走。第一步在测试环境用hping3模拟攻击观察日志里count字段的实际数值记下攻击时的峰值。第二步在正常业务时段跑一天记录正常流量的最高值。第三步把阈值设在正常峰值和攻击峰值之间留出 20% 到 30% 的余量。# 用 hping3 模拟 SYN 洪水测试检测阈值 sudo hping3 -S --flood -V -p 80 目标IP # 模拟端口扫描 sudo nmap -sS -p 1-1000 目标IP跑完测试后去查Database.py对应的表看看告警记录里的count值再回头改Main.py里的阈值。这个过程可能要反复两三次但调好之后系统就稳了。5. 避坑与排查那些让我半夜爬起来改代码的问题5.1 抓不到包控制台一片安静现象Main.py启动后没有任何告警输出用nmap扫也检测不到。原因最常见的是网卡名填错或者程序没有以 root 运行。原始套接字在非 root 下会静默失败不报错但也不抓包。另一个可能是 iptables 规则在抓包之前就把包 DROP 了导致抓包线程看不到。解决先用ip addr确认网卡名再用sudo启动。如果还不行临时清空 iptables 规则sudo iptables -F排除干扰后再测。5.2 误封正常服务器业务中断现象内网一台监控服务器被反复封禁导致监控数据断传。原因DOS_THRESHOLD设得太低监控服务器的心跳连接频率刚好超过阈值。或者TIME_WINDOW太短正常突发流量被误判。解决查Database.py里的告警记录看被误封 IP 的count值把DOS_THRESHOLD往上调至少留出 30% 余量。同时把TIME_WINDOW从 10 秒调到 30 秒平滑突发流量。5.3 iptables 规则不生效封了还能连现象日志显示已封禁但攻击机还能访问目标端口。原因iptables 规则插入的位置不对被前面的 ACCEPT 规则截胡了。或者规则加在了错误的链上比如加到了 OUTPUT 而不是 INPUT。解决用sudo iptables -L INPUT -n --line-numbers查看规则顺序确保 DROP 规则在 ACCEPT 之前。如果前面有ACCEPT all用-I插入到最前面而不是-A追加到最后。5.4 数据库写入失败告警丢失现象控制台有告警打印但Database.py对应的表里查不到记录。原因SQLite 在多线程下写入需要加锁源码里如果没处理并发抓包线程和分析线程同时写就会报database is locked。解决在Database.py里给写操作加线程锁或者把数据库操作单独放到一个线程里串行执行。简单做法是用threading.Lock()包住INSERT语句。5.5 程序跑一段时间后内存暴涨现象运行几小时后Main.py占用内存越来越大最后被系统杀掉。原因syn_count和conn_count这两个字典只增不减攻击 IP 多了之后内存就爆了。源码里如果只在时间窗口到期时clear()但窗口内新 IP 不断进来还是会涨。解决在clear()的同时给字典设一个上限超过就淘汰最旧的条目。或者用collections.OrderedDict配合popitem(lastFalse)做 LRU 淘汰。6. 进阶技巧把检测日志变成可复用的防御资产跑通之后别只盯着控制台看。Database.py里积累的告警数据才是真正有价值的东西。我一般会加一个定时脚本每小时把封禁记录导出成 CSV然后用 Python 做一次聚合分析看看哪些 IP 是惯犯、哪些时间段攻击最密集。这样下次调阈值的时候就有数据支撑不用再靠猜。# 导出封禁记录并统计 Top 10 攻击源 import sqlite3 import csv from collections import Counter conn sqlite3.connect(ids.db) cursor conn.cursor() cursor.execute(SELECT src_ip, attack_type FROM alerts WHERE blocked1) rows cursor.fetchall() # 统计每个 IP 被封禁的次数 counter Counter([row[0] for row in rows]) for ip, count in counter.most_common(10): print(f{ip}: {count} 次) # 导出到 CSV 备查 with open(blocked_history.csv, w, newline) as f: writer csv.writer(f) writer.writerow([src_ip, attack_type]) writer.writerows(rows) conn.close()这段脚本跑完你会得到一份攻击源排行。如果某个 IP 反复出现可以考虑在 iptables 里做永久封禁而不是等它每次触发阈值再封。另外把attack_type和timestamp结合起来看能发现攻击的时间规律比如每天凌晨集中扫描那就可以在那个时段临时调低阈值提高检出率。还有一个技巧是给Flitter.py加一个白名单机制。内网的核心服务器、网关、监控节点这些 IP 不应该被自动封禁。做法很简单在执行 iptables 命令之前先判断来源 IP 是否在白名单里# Flitter.py 中增加白名单判断 WHITELIST [192.168.1.1, 192.168.1.100, 10.0.0.5] def block_ip(src_ip): if src_ip in WHITELIST: print(f{src_ip} 在白名单中跳过封禁) return # 执行 iptables 封禁 import os os.system(fiptables -I INPUT -s {src_ip} -j DROP)白名单一定要在部署前就配好别等误封了再补那时候业务已经断了。我现在的习惯是任何联动 iptables 的脚本上线前先跑一遍白名单校验确认核心 IP 都在列表里才敢让它自动封禁。从那以后我每次部署这类系统都强制走一遍白名单检查再也没出现过半夜被叫起来解封的情况。希望这套源码和这些踩坑记录能帮到你少走点弯路。本文还有配套的精品资源点击获取