简介这是一套基于Python的漏洞扫描系统毕业设计/课程设计资源包面向计算机专业学生与网络安全学习者帮助理解漏洞检测、端口扫描、Web安全等完整流程。项目以PythonDjango实现涉及网络探测、漏洞匹配、结果分析等核心模块也包含SQL注入、XSS、CSRF等常见漏洞类型的检测思路并使用扫描工具完成主机发现与端口探测适合课程实践或毕业论文研究。压缩包共374个文件大小74.98MB包含Python源码及编译文件、前端页面资源、演示图片、数据库脚本和可执行程序素材完整便于复现。包内另附安装依赖的视频教程和数据库、程序等目录能辅助快速搭建环境、核对数据结构并运行系统。该资源已有648人学习下载对毕业设计选题、漏洞扫描开发入门和网络安全能力提升都很有参考价值。1. 为什么还要自己写一个漏洞扫描器从 Nmap 到 Web 报告的最后一公里搞过安全工作或者做过网络安全方向毕业设计的人基本都被同一件事卡过Nmap、OpenVAS 这些工具扫描能力很强但结果是一堆 XML 或纯文本拿给导师、甲方或者期末答辩评委看根本不直观。而 Nessus 这类商业产品又贵又重装一次够折腾半天。这份“基于 Python 的漏洞扫描系统”解决的就是这个断层——把端口扫描、服务识别、漏洞指纹匹配和 Web 报告捏合成一条完整的链路。它的核心逻辑是用 Python 自研扫描引擎借助 Django 把检测过程变成可视化的 Web 应用扫码即得结果。适合三类人做毕业设计需要完整前后端项目的学生、刚入行想弄懂扫描器内部原理的安全从业者、以及手里有台 Linux 服务器想搞内网资产普查的运维。它不试图替代 Nmap而是让你看懂一套扫描器从 socket 探活到漏洞判定的完整实现路径。2. 先把项目跑起来资源结构、依赖安装与初始化拿到压缩包的第一件事不是急着看代码而是把目录结构摸清楚。这份资源里除了 Python 源码还带了一份视频教程“扫描需要安装的包视频-必须操作”名字里就写着“必须操作”说明作者自己也清楚这项目的依赖安装环节容易翻车。目录里同时出现了 nikto.1、nikto.conf 和一堆 CSS 文件bootstrap.css、layui.css、font-awesome.css这透露了一个重要信息扫描器的漏洞规则库参考了 nikto 的指纹定义而前端直接用了 Layui 和 Bootstrap 两套框架。2.1 先盘一下压缩包里的真实结构解压之后我建议你按下面的清单核对一遍文件缺什么心里有数路径类型作用扫描需要安装的包视频-必须操作视频依赖安装演示建议先看一遍再动手数据库/目录Django 的数据库迁移文件含模型定义程序/目录扫描器核心源码含 Django 工程和应用nikto.1 / nikto.conf文件nikto 的规则参考与格式样例用于理解漏洞指纹写法bootstrap.css / layui.css / font-awesome.css静态文件Web 前端样式报告页面的展示基础这里有两个点要特别留意。第一nikto.conf 不是直接给 Python 用的它是拿来对照参考的——你需要理解 nikto 的漏洞规则是怎么组织的然后在自己的扫描引擎里按类似结构定义检测项。第二前端用的是 Layui 而不是纯 Bootstrap这说明项目的管理后台和报告页面可能不是一个前端技术栈改样式时别找错文件。2.2 安装步骤与常见依赖项先看视频里演示的安装顺序再按下面的流程操作。我的建议是直接用虚拟环境别把依赖装进系统 Python否则后续折腾 Django 版本时会哭。# 1. 创建并激活虚拟环境Windows 用 venvLinux/macOS 同样适用 python -m venv scan_env # Windows 下激活 scan_env\Scripts\activate # Linux/macOS 下激活 source scan_env/bin/activate # 2. 安装核心依赖注意加 -i 指定国内镜像否则可能卡在超时上 pip install django3.2.25 -i https://pypi.tuna.tsinghua.edu.cn/simple pip install requests beautifulsoup4 sqlalchemy -i https://pypi.tuna.tsinghua.edu.cn/simple pip install python-nmap -i https://pypi.tuna.tsinghua.edu.cn/simple # 3. 进入项目目录并完成 Django 迁移 cd 程序 python manage.py makemigrations python manage.py migrate # 4. 启动 Web 服务 python manage.py runserver 0.0.0.0:8000每一条命令都有讲究。虚拟环境这一步是为了隔离项目依赖防止 Django 版本和系统里其他项目冲突指定清华镜像是因为 pypi 官方源在国内的连通性经常不稳定换成镜像后下载速度能快一个量级makemigrations 和 migrate 这两步是 Django 的固定动作前者根据 models.py 生成迁移脚本后者把表结构真正写入数据库——如果跳过了直接 runserver启动时会直接报 “No migrations applied” 或者页面里全是表不存在的异常。依赖里除了 Djangorequests 是给扫描模块发 HTTP 探测用的BeautifulSoup 负责解析响应体里的关键内容SQLAlchemy 在报告导出和数据分析时用得上python-nmap 则是调用本机 Nmap 做端口扫描的桥梁。2.3 启动后的第一眼验证服务跑起来后浏览器访问 http://127.0.0.1:8000应该能看到登录页或任务管理页。如果页面样式是乱的、CSS 全丢了大概率是静态文件没有收集——执行一条python manage.py collectstatic就能解决。到这里项目就算跑通了接下来才是重头戏搞清楚扫描器内部在做什么。3. 扫描引擎拆解从端口探活到服务指纹识别这套系统的扫描引擎不是用别人的 SDK 一包了之而是自己实现了一套从 TCP 连接到服务判定的链路。理解这段代码是你能在答辩里讲清楚“系统是自己写的”的关键证据。整个流程分三步并发端口扫描、banner 抓取、服务指纹匹配最后才是漏洞规则匹配。3.1 端口扫描模块socket 并发与超时控制端口扫描是扫描器的基础层。这个项目用的是 Python 的 socket 原生实现配合线程池做并发。看核心代码import socket import concurrent.futures as futures def tcp_scan_port(host, port, timeout3): 对单个端口执行 TCP connect 扫描 host: 目标 IP 或域名 port: 目标端口 timeout: 连接超时时间单位秒 try: sock socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(timeout) result sock.connect_ex((host, port)) sock.close() return port, (result 0) except socket.error: return port, False def scan_ports(host, ports, max_workers100): 并发扫描多个端口 ports: 端口列表 max_workers: 线程数控制并发力度 open_ports [] with futures.ThreadPoolExecutor(max_workersmax_workers) as executor: future_map {executor.submit(tcp_scan_port, host, p): p for p in ports} for future in futures.as_completed(future_map): port, is_open future.result() if is_open: open_ports.append(port) return sorted(open_ports)这段代码有三个参数是关键timeout、max_workers 和端口范围。timeout 默认设 3 秒局域网内够用但扫公网或者延迟高的目标时建议调到 5-6 秒否则丢端口max_workers 控制并发线程数默认 100对个人电脑和普通服务器都安全如果扫的目标网段很大可以调到 200但再高就容易触发系统层面的文件描述符限制了。connect_ex 方法比 connect 好在不抛异常而是通过返回值判断端口状态返回值 0 代表连接成功非 0 就是不可达。3.2 服务指纹识别用 banner 猜出目标在跑什么服务端口开放只是第一步知道端口背后跑的是什么服务才有意义。这套系统抓取服务 banner 的方式是连接成功后主动发送探测字符串然后从响应里匹配特征。看代码def get_service_banner(host, port, timeout3): 连接目标端口并发送探测报文读取 banner 信息 返回格式: (端口, 服务名, 完整banner字符串) probes { 80: bGET / HTTP/1.1\r\nHost: {host}\r\nConnection: close\r\n\r\n, 22: bSSH-2.0-OpenSSH_Probe\r\n, 443: bGET / HTTP/1.1\r\nHost: {host}\r\nConnection: close\r\n\r\n, } default_probe b\r\n\r\n try: sock socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(timeout) sock.connect((host, port)) probe probes.get(port, default_probe).format(hosthost).encode() sock.send(probe) banner sock.recv(1024).decode(utf-8, errorsignore).strip() sock.close() service match_service_fingerprint(banner, port) return port, service, banner except Exception: return port, unknown, 探测字符串的构造是有讲究的对 Web 端口发 HTTP GET 请求对 SSH 端口发送客户端的版本声明这样才能诱使服务端把版本信息吐出来。match_service_fingerprint 是一个指纹匹配函数内部维护了一个「关键词到服务名」的映射表比如 banner 里出现 nginx 就判定为 nginx出现 Apache 就判定为 Apache。这个映射表在实际项目中就是对照 nikto.conf 里的服务指纹段抄出来的不需要自己从零发明。3.3 漏洞规则匹配指纹命中后的判定逻辑拿到了服务名和版本号漏洞检测就变成了一个查表过程。系统的规则库维护着(服务名, 版本范围, CVE编号, 风险等级, 描述)这样的结构判定逻辑如下def check_vulns(service, version, vuln_db): service: 服务名称如 nginx version: 版本号如 1.14.2 vuln_db: 漏洞规则库list of dict hits [] for rule in vuln_db: # rule 结构示例 # {service: nginx, affected_versions: 1.18.0, # cve: CVE-2021-23017, risk: 高危, desc: DNS 解析拒绝服务} if rule[service] service and version_in_range(version, rule[affected_versions]): hits.append(rule) return hitsversion_in_range 是自己实现的版本比较函数因为字符串形式的版本号不能直接用字典序比较需要按照「主版本号、次版本号、修订号」逐段拆开后按整数比。这个模块是整个系统里最容易翻车的地方——很多人图省事直接拿字符串比结果 “1.9.0” 被判定成大于 “1.10.0”误报和漏报全来了。4. 业务层落地Django 模型设计、任务编排与报告生成扫描引擎跑通了下一步是把能力包装成 Web 应用。这个项目用 Django 做业务层涉及三个核心模块数据模型定义、扫描任务调度、报告展示与导出。这也是毕业设计答辩时导师最关注的环节——能不能讲清楚数据怎么流转、任务怎么管理。4.1 数据模型设计任务、主机、端口、漏洞四张表数据库设计决定了整个系统能记录多少信息。这套系统最核心的模型关系是「一个扫描任务关联多个主机一个主机关联多个开放端口每个端口可以命中多条漏洞记录」。对应 Django 的 models.py 核心代码from django.db import models class ScanTask(models.Model): 扫描任务表一次扫描的完整记录 name models.CharField(max_length100, verbose_name任务名称) target models.CharField(max_length255, verbose_name扫描目标) status models.CharField(max_length20, defaultpending, verbose_name任务状态) created_at models.DateTimeField(auto_now_addTrue, verbose_name创建时间) class Host(models.Model): 主机表任务下扫描到的主机 task models.ForeignKey(ScanTask, on_deletemodels.CASCADE, related_namehosts) ip models.GenericIPAddressField(verbose_name主机IP) os_guess models.CharField(max_length100, blankTrue, verbose_name系统指纹) class PortRecord(models.Model): 端口记录表主机上开放的端口及服务信息 host models.ForeignKey(Host, on_deletemodels.CASCADE, related_nameports) port models.IntegerField(verbose_name端口号) service models.CharField(max_length50, defaultunknown, verbose_name服务名称) class VulnRecord(models.Model): 漏洞记录表命中的漏洞信息 port models.ForeignKey(PortRecord, on_deletemodels.CASCADE, related_namevulns) cve_id models.CharField(max_length20, verbose_nameCVE编号) risk_level models.CharField(max_length10, verbose_name风险等级) description models.TextField(verbose_name漏洞描述)四张表的关系是层级递进的ScanTask 可以包含多个 Host一个 Host 下有多条 PortRecord每条 PortRecord 又可以命中多个 VulnRecord。用 ForeignKey 关联的好处是可以在 Django 管理后台里一层层往下钻取数据从任务直接点到漏洞详情这对演示效果帮助很大。repr 里的 related_name 参数是给反向查询起名字比如task.hosts.all()就是拿一个任务下的全部主机命名不写写的话 Django 会自动生成model_set代码可读性会差一截。4.2 任务编排从提交目标到扫描完成的完整流程用户在页面上提交一个 IP 或域名后Django 视图层要做的事是先创建 ScanTask 记录然后调起扫描引擎跑端口扫描、服务识别、漏洞匹配每一步结果写入对应的表。这个过程可以是同步的——用户提交后页面等待扫描完成也可以是异步的——任务在后台跑用户通过刷新页面看状态。这个项目的初始版本用的同步方式代码如下from django.shortcuts import render from .models import ScanTask, Host, PortRecord, VulnRecord from .scanner import scan_ports, get_service_banner, check_vulns def start_scan(request): if request.method POST: target request.POST.get(target) # 1. 创建任务 task ScanTask.objects.create(namefscan-{target}, targettarget, statusrunning) # 2. 扫描常见端口 open_ports scan_ports(target, [21, 22, 23, 25, 80, 110, 143, 443, 3306, 3389, 8080, 8443]) # 3. 创建主机记录 host Host.objects.create(tasktask, iptarget) for port in open_ports: service, banner get_service_banner(target, port) port_obj PortRecord.objects.create(hosthost, portport, serviceservice) vulns check_vulns(service, banner, load_vuln_db()) for v in vulns: VulnRecord.objects.create(portport_obj, cve_idv[cve], risk_levelv[risk], descriptionv[desc]) task.status completed task.save() return render(request, result.html, {task: task}) return render(request, scan.html)这段代码里值得注意的细节端口列表写死了 21、22、23 这类常见端口这是因为扫描系统面向的教学场景不是做全网端口普查如果你想扫描更多端口可以改成传入一个 range 区间。get_service_banner 返回的是二元组但在实际调用中写的是service, banner 这要求函数签名跟上一章的版本保持一致如果你自己魔改过扫描模块这里要留意解包参数的数量。4.3 报告生成页面渲染与 CSV 导出报告模块是这个项目区别于纯后端扫描器的价值点。页面端用 Django 模板渲染结果展示任务基本信息、端口开放情况、漏洞列表和风险等级。同时系统支持导出 CSV 报告方便往毕业论文里贴数据import csv from django.http import HttpResponse def export_report(request, task_id): task ScanTask.objects.get(idtask_id) response HttpResponse(content_typetext/csv) response[Content-Disposition] fattachment; filenamescan_report_{task_id}.csv writer csv.writer(response) writer.writerow([IP, 端口, 服务, CVE编号, 风险等级, 描述]) for host in task.hosts.all(): for port in host.ports.all(): for vuln in port.vulns.all(): writer.writerow([host.ip, port.port, port.service, vuln.cve_id, vuln.risk_level, vuln.description]) return response这个导出函数把四张表的数据横向展开成一张扁平的 CSV每一行是一条漏洞记录。Content-Disposition 头里的 attachment 参数是告诉浏览器“这是一个下载文件”而不是“在页面里打开”。给论文配图的时候用这个导出的 CSV 配合 Excel 做个透视表能画出端口分布饼图和风险等级柱状图。5. 踩坑记录与排查清单依赖、并发、误报与数据库锁这部分按“现象 → 原因 → 解决”的格式整理这份资源最容易翻车的 5 个点。我拆过不少类似的毕业设计项目这些问题百分之百会遇到越早看到越省时间。5.1 现象pip install 卡在 “Building wheel” 不动原因部分依赖需要源码编译而本机缺少编译工具链。最常见的是 python-nmap 安装时拉取依赖超时或者是 Django 版本和 Python 版本不匹配——比如 Python 3.11 配 Django 2.2 就有兼容问题。解决先确认 Python 版本是 3.8 或 3.9然后统一用清华源安装全部依赖。如果某个包编译失败到 https://pypi.org/project/ 看这个包是否有 Windows 预编译的 wheel 文件有的话直接下载 whl 文件本地安装。5.2 现象runserver 启动成功页面却报 “table doesn’t exist”原因makemigrations 只生成了迁移文件没有执行 migrate 把表建进数据库。很多人执行到第二步就停了结果页面访问任何模型相关的数据都直接报 500 或 OperationalError。解决在项目目录下执行python manage.py migrate如果之前已经启动过服务导致半中间状态先删掉项目下的 db.sqlite3 文件再重新 migrate——反正初始环境没什么值钱的数据。5.3 现象扫同一台机器结果和 Nmap 不一致总是少端口原因自带的常见端口列表只有 12 个而目标服务器的服务可能跑在 8001、9000、6379 这类非主流端口上端口列表覆盖不到。另一个原因是 timeout 设太短高延迟网络下连接握手超过了 3 秒就被判定为超时过滤掉了。解决去start_scan视图里把端口列表改成一个 range比如range(1, 1024)先跑一遍全扫确认哪些端口是稳定的再用精简列表做后续重复扫描。timeout 调大到 5 秒同时把 max_workers 降到 50减少网络抖动的影响。5.4 现象漏洞判定全是“未知”或“无漏洞”但 Nmap 能查到原因漏洞规则库太薄了。资源的规则库只覆盖了几十个常见 CVE 条目服务版本只要不在库里的受影响范围内就会被判成“无漏洞”。Nmap 的 NSE 脚本库有几千条检测脚本自然查得多。解决要么自己往漏洞数据库里补 CVE 和受影响版本范围要么去 CVE 官网下载公开的 CPE 数据用脚本导入到系统的规则表里。补数据的格式照着 check_vulns 接收的 dict 结构写就行。5.5 现象数据库频繁报 “database is locked”原因SQLite 只允许一个进程写数据。扫描任务跑起来后多线程并发写 PortRecord 和 VulnRecord再加上用户同时打开了报告页面触发读操作SQLite 就会锁库。解决有两个方案。第一个是暴力方案在数据库连接参数里加timeout30让写入等待锁释放——但这个方案对长时间扫描依然不保险。第二个是规范方案把扫描任务的写入改成单线程串行也就是在 views 里先把扫描结果收集到内存列表扫描全部结束再一次性写入数据库这样只发生一次写操作锁冲突概率大大降低。6. 验证与扩展用本地靶机跑一遍再往实战方向改造项目跑通不等于能用。我验证这类扫描器有一个固定套路本地起一台 Metasploitable 2 或者 DVWA 靶机用它当目标跑完整扫描拿结果对照 Nmap 的输出。这比直接扫外网安全得多也不会触碰到授权边界。6.1 靶机验证的操作方法VMware 里导入 Metasploitable 2 镜像网络模式设为仅主机模式这样宿主机可以直接访问靶机 IP又不会暴露给物理网络。然后在系统的扫描页面填靶机 IP提交任务观察扫描结果172.16.56.128 这台靶机默认开了 21、22、23、25、80、3306 等端口扫出来应该和靶机的服务清单一一对应系统如果命中 vsftpd 2.3.4 的 CVE-2011-2523会记一条高危漏洞记录——这个 CVE 在 Metasploitable 2 里是标配验证规则库是否有能力覆盖。对照方法是nmap -sV -p 1-1000 靶机IP把 Nmap 识别的服务版本和系统的 banner 判定结果放在一起比。如果出现版本号不一致优先检查系统抓 banner 用的探测字符串是不是被目标服务器的防火墙或者特殊配置干扰了。6.2 三个扩展方向建议按需选做验证通过之后如果想让这个项目从“毕业设计作品”变成“自用工具”以下几个扩展点是高性价比的第一接外部 CVE 数据源。抓 CIRCL CVE API 或 NVD 的 JSON 数据定期同步到本地漏洞库避免规则停留在 2020 年之前的旧数据上。第二把同步扫描改成异步任务。用 Celery 或简单的threading.Thread把扫描放到后台跑页面立即返回“任务已创建”用户刷新查看进度体验提升非常明显——代价是需要处理线程安全建议把 SQLite 换成 PostgreSQL。第三生成 HTML 格式报告。CSV 导出只是勉强够用写一个report.html模板把扫描结果渲染成带表格和颜色标记的完整报告论文附录可以直接截图用。我从那次拆解起养成一个习惯任何扫描器项目拿到手第一件事不是读代码而是先起一台靶机跑一遍全流程确认扫描链路是通的再去看具体实现。这不是浪费时间而是给自己建立一条验证基线——项目能不能用跑一遍全知道了。希望这份拆解能帮你省掉几个晚上的踩坑时间。本文还有配套的精品资源点击获取