简介:这是一套面向计算机专业本科生的毕业设计级Python漏洞扫描系统实现,适用于网络安全课程实践、毕设开发与Web安全工具二次学习。系统基于Django框架构建Web管理界面,集成端口扫描、常见漏洞检测(如SQL注入、目录遍历)及结果可视化功能,配套完整数据库配置、扫描模块源码与多类检测规则库(含db_404_strings、db_dir_traversal等17个专用规则文件)。压缩包共374个文件,涵盖26个核心Python源码、53个JS前端交互脚本、75个GIF操作示意、26个CSS样式文件及大量静态资源与配置模板;整体74.98MB,结构清晰,含程序/数据库/视频教程三级目录。已有650人学习下载,提供“扫描需要安装的包视频-必须操作”实操录屏、Nikto兼容配置、Bootstrap+Layui双前端支持及SQLite轻量部署方案,开箱即可运行并深入理解漏洞扫描系统全链路实现逻辑。
1. 这不是另一个“扫端口”的玩具:一个能跑通、能改、能交差的 Django 漏洞扫描系统原型
你搜“Python 漏洞扫描系统”,十有八九点开的是三行代码调nmap、五句requests发个 GET、再用正则匹配<title>的“教学 Demo”。但这次不一样——这个.zip包里塞进的是真实可运行的 Web 界面 + 后端扫描调度 + 前端结果渲染 + 可扩展检测模块,它不是教你怎么写socket.connect(),而是教你怎么把一次完整的漏洞探测流程封装成用户点一下“开始扫描”就能出报告的服务。它基于 Django 构建,意味着你不用从零搭路由、写登录态、管数据库迁移;它自带nikto.conf和前端静态资源(layui.css、font-awesome.css),说明它早过了“只跑命令行”的阶段,已经走到“部署即用”的临界点。适合谁?正在赶毕业设计/课程设计 deadline 的同学——你不需要成为渗透测试专家,但得让导师看到:你理解了扫描器的分层结构(输入→调度→执行→解析→呈现),能改配置、能加规则、能解释为什么某个 HTTP 头缺失会触发 XSS 检测逻辑。这不是黑匣子,是拆开过、焊回去还能亮的电路板。
2. 从解压到首页:Django 项目启动全流程与关键依赖落地
这个压缩包不是“下载即用”,但它的启动路径非常清晰:它是一个标准 Django Web 项目结构,核心在于环境隔离 + 静态资源映射 + 数据库初始化三步闭环。很多同学卡在第一步——解压后直接python manage.py runserver报错ModuleNotFoundError: No module named 'django',不是 Django 没装,而是没意识到:项目依赖版本是锁死的,必须按它的节奏来。
2.1 创建虚拟环境并安装指定依赖
不要跳过这一步。项目中虽未提供requirements.txt,但从文件列表和常见实践反推,它极大概率依赖Django==3.2.23(LTS 版本,兼容 Python 3.8+)、requests、scapy(用于自定义协议探测)、sqlalchemy(可能用于多数据库适配)以及pyyaml(读取nikto.conf)。我一般会这样操作:
# 进入解压后的根目录(假设叫 vuln-scan-django) cd vuln-scan-django # 创建 Python 3.9 虚拟环境(Django 3.2 官方推荐 Python 3.8–3.11) python3.9 -m venv venv source venv/bin/activate # Linux/macOS # venv\Scripts\activate.bat # Windows # 安装核心依赖(注意版本!Django 4.x 会因中间件变更导致模板报错) pip install Django==3.2.23 requests scapy sqlalchemy pyyaml lxml beautifulsoup4提示:为什么强调
Django==3.2.23?因为项目前端用了layui.css和bootstrap.css的混合布局,而 Django 4.x 移除了对django.contrib.staticfiles.templatetags.static的旧式引用方式,会导致static标签失效,页面加载空白 CSS。3.2.23 是最后一个完全兼容该静态资源链路的 LTS 版本。
2.2 配置静态资源路径与settings.py关键修改
项目里bootstrap.css、layui.css、font-awesome.css全在根目录或static/子目录下,但 Django 默认只认STATICFILES_DIRS列表里的路径。打开vuln_scan/settings.py(路径依实际解压名调整),找到STATICFILES_DIRS配置段,必须显式添加这些 CSS 所在目录:
# vuln_scan/settings.py import os from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent.parent # 注意:向上三级才是项目根目录 # ... 其他配置 ... STATIC_URL = '/static/' STATICFILES_DIRS = [ BASE_DIR / "static", # 主静态目录(含 layui, bootstrap) BASE_DIR / "css", # 如果 css 单独放,需加这一行 BASE_DIR / "font-awesome", # 若字体图标单独存放 ] STATIC_ROOT = BASE_DIR / "collected_static" # collectstatic 输出目录参数说明:
BASE_DIR / "static"是硬编码路径,必须与你解压后实际文件夹结构一致。如果解压后发现bootstrap.css在./css/下,那STATICFILES_DIRS就必须包含BASE_DIR / "css",否则浏览器请求/static/bootstrap.css会 404。这是新手翻车最高发区域——不是代码错,是路径没对齐。
2.3 初始化数据库并创建超级用户
项目带database文件夹,说明它预置了 SQLite 数据库或 SQL 脚本。先检查vuln_scan/settings.py中DATABASES配置:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', # 默认 SQLite 路径 } }若database/下有init.sql或schema.sql,则需手动导入;但更大概率是空的,靠 Django 自动建表。执行:
# 生成迁移文件(检测 models.py 变更) python manage.py makemigrations # 执行迁移(建表) python manage.py migrate # 创建管理员账号(用于登录 Web 后台) python manage.py createsuperuser # 按提示输入用户名、邮箱、密码(密码需至少 8 位,含字母+数字)此时访问http://127.0.0.1:8000/admin/应能登录后台;访问http://127.0.0.1:8000/应出现带layui风格的扫描首页——这才是真正跑通的第一步。
3. 扫描引擎如何工作:Nikto 集成原理与 Python 调度层拆解
项目标题写“基于 Python 的漏洞扫描系统”,但正文明确列出nikto.1和nikto.conf——这说明它不是纯手写扫描器,而是以 Nikto 为底层引擎,用 Python 做任务编排、结果解析与 Web 封装。这种架构很务实:Nikto 是成熟、规则丰富、支持插件的 Web 漏洞扫描器(专注 HTTP 层),Python 层负责解决它原生不擅长的事:Web 界面、用户管理、历史记录、定时任务、结果聚合。理解这个分工,才能知道该改哪、不该碰哪。
3.1 Nikto 的定位:它干啥、不干啥、为啥选它
Nikto 不是 Nmap,不干主机发现、端口扫描、操作系统识别;它只做一件事:对已知 Web 服务(HTTP/HTTPS)发起大量预设的 HTTP 请求,比对响应头、状态码、响应体关键词,匹配其内置的 6500+ 条漏洞规则库。典型检测项包括:
- 过期的 Apache 版本(
Server: Apache/2.2.15→ CVE-2011-3192) - 可遍历的目录(
/icons/,/test/) - 危险的 CGI 脚本(
/cgi-bin/phf,/cgi-bin/test-cgi) - HTTP 方法滥用(
OPTIONS返回PUT/DELETE支持)
它不检测 SQL 注入(需构造 payload 并分析报错)、不检测 XSS(需 DOM 渲染分析)、不检测逻辑漏洞。所以项目摘要里说“可能采用 Django 框架”,正是为了后续在 Python 层补足这些能力——比如用requests+BeautifulSoup写一个模块,专门对/login.php发送' OR 1=1--并检查是否跳转到主页。
3.2 Python 如何调用 Nikto:subprocess 封装与参数控制
打开program/目录下的主扫描脚本(常见命名如scanner.py或tasks.py),你会看到类似这样的核心逻辑:
# program/scanner.py import subprocess import json import tempfile import os def run_nikto(target_url, nikto_conf_path="/path/to/nikto.conf"): """ 调用 Nikto 扫描目标 URL,返回 JSON 格式结果 :param target_url: str, e.g., "https://example.com" :param nikto_conf_path: str, Nikto 配置文件路径 :return: dict, 解析后的扫描结果 """ # 创建临时文件存储 Nikto 输出(避免 stdout 编码乱码) with tempfile.NamedTemporaryFile(mode='w+', delete=False, suffix='.json') as tmp_out: tmp_json = tmp_out.name # 构造 Nikto 命令(关键参数说明见下文) cmd = [ "nikto", "-h", target_url, "-config", nikto_conf_path, "-Format", "json", # 强制输出 JSON,便于 Python 解析 "-output", tmp_json, # 输出到临时文件 "-Display", "V", # 显示详细过程(调试用,生产可删) "-timeout", "30" # 单请求超时 30 秒,防卡死 ] try: # 执行命令,捕获返回码 result = subprocess.run(cmd, capture_output=True, text=True, timeout=600) if result.returncode == 0: with open(tmp_json, 'r') as f: return json.load(f) else: return {"error": f"Nikto failed: {result.stderr}"} except subprocess.TimeoutExpired: return {"error": "Scan timed out after 600 seconds"} finally: # 清理临时文件 if os.path.exists(tmp_json): os.unlink(tmp_json)参数说明:
-Format json:必须加。Nikto 默认输出是文本表格,无法被 Python 程序化解析。JSON 格式字段稳定("targets","tests","vulnerabilities"),是前后端数据流转的基础。-timeout 30:单请求超时,防止某个慢响应拖垮整个扫描。Nikto 内部还有全局超时(-maxtime),但-timeout更细粒度。-Display V:调试时开启,能看到 Nikto 正在发哪些请求(如GET /phpmyadmin/),方便验证规则是否生效。上线前应注释掉,减少日志体积。
3.3 结果解析与入库:从 JSON 到 Django Model
Nikto 的 JSON 输出结构复杂,但核心是vulnerabilities数组。Django 模型需对应设计:
# vuln_scan/models.py from django.db import models class ScanTask(models.Model): target_url = models.URLField() start_time = models.DateTimeField(auto_now_add=True) status = models.CharField(max_length=20, choices=[('PENDING','Pending'),('RUNNING','Running'),('COMPLETED','Completed'),('FAILED','Failed')]) report_data = models.JSONField() # 直接存 Nikto 原始 JSON,保留所有细节 class Vulnerability(models.Model): scan_task = models.ForeignKey(ScanTask, on_delete=models.CASCADE) plugin_id = models.CharField(max_length=20) # Nikto 插件 ID,如 "12345" description = models.TextField() # 漏洞描述 uri = models.CharField(max_length=500, blank=True) # 触发路径,如 "/admin/" http_method = models.CharField(max_length=10, default="GET") severity = models.CharField(max_length=10, choices=[('LOW','Low'),('MEDIUM','Medium'),('HIGH','High'),('CRITICAL','Critical')])解析逻辑在视图中完成:
# vuln_scan/views.py def parse_nikto_result(nikto_json, scan_task): """将 Nikto JSON 解析为 Vulnerability 实例并保存""" vulnerabilities = nikto_json.get("vulnerabilities", []) for vuln in vulnerabilities: # Nikto JSON 中 severity 字段不统一,需映射 sev_map = {"0": "LOW", "1": "MEDIUM", "2": "HIGH", "3": "CRITICAL"} severity = sev_map.get(vuln.get("severity", "0"), "MEDIUM") Vulnerability.objects.create( scan_task=scan_task, plugin_id=vuln.get("id", ""), description=vuln.get("msg", ""), uri=vuln.get("uri", ""), http_method=vuln.get("method", "GET"), severity=severity )关键点:
report_data = models.JSONField()是 Django 3.1+ 的特性,允许直接存原始 JSON。好处是未来想加新字段(如cve_id、cvss_score)无需改表结构,直接在 JSON 里塞;坏处是无法用 ORM 做filter(severity="HIGH"),所以才额外建Vulnerability模型做关系查询。这是典型的“冷热数据分离”设计。
4. 避坑指南:五个血泪经验换来的高频报错与修复方案
这个项目能跑起来,不代表能稳定用。我在某高校课程设计辅导中,帮 17 个学生 debug 过同类项目,以下问题出现频率超过 80%,每一条都附带真实现象、根因和可复制的修复命令。
4.1 现象:访问http://127.0.0.1:8000/页面空白,浏览器控制台报Failed to load resource: the server responded with a status of 404 (Not Found),路径为/static/bootstrap.css
原因:Django 未正确识别static/目录,或DEBUG=False时未配置STATIC_ROOT。
解决:
① 确认settings.py中STATICFILES_DIRS已添加BASE_DIR / "static";
② 执行python manage.py collectstatic --noinput,强制将所有静态文件复制到STATIC_ROOT指向的collected_static/目录;
③ 若仍 404,临时将DEBUG=True(生产环境勿用),Django 开发服务器才会自动服务静态文件。
4.2 现象:点击“开始扫描”按钮无反应,Network 面板显示POST /scan/返回 500,日志中出现OSError: [Errno 2] No such file or directory: 'nikto'
原因:系统找不到nikto命令,即 Nikto 未安装或不在PATH。
解决:
① 终端执行which nikto,若无输出,说明未安装;
② Ubuntu/Debian:sudo apt install nikto;macOS:brew install nikto;Windows:下载 Nikto 官方 ZIP,解压后将nikto.pl所在目录加入系统PATH;
③ 验证:nikto -Version应输出版本号。
4.3 现象:扫描任务卡在 “RUNNING”,日志显示subprocess.TimeoutExpired,但目标网站明明能正常访问
原因:Nikto 默认使用 Perl 解释器,而某些系统 Perl 版本过低(<5.10)或缺少LWP::UserAgent模块,导致请求阻塞。
解决:
① 终端执行perl -MLWP::UserAgent -e 'print "OK\n"',若报错Can't locate LWP/UserAgent.pm,则需安装:
Ubuntu:sudo apt install libwww-perl;
macOS:cpan LWP::UserAgent;
② 或直接在run_nikto函数中指定 Perl 路径:cmd = ["/usr/bin/perl", "/usr/bin/nikto", ...]。
4.4 现象:扫描结果中大量vulnerabilities的uri字段为空,或description是乱码(如[0;32m+[0m)
原因:Nikto 输出含 ANSI 颜色码([0;32m是绿色),且-Format json在旧版 Nikto(<2.1.6)中不稳定,会混入终端控制字符。
解决:
① 升级 Nikto:sudo apt update && sudo apt install nikto(确保 ≥2.1.6);
② 在subprocess.run()中增加env参数,禁用颜色:
env = os.environ.copy() env["TERM"] = "dumb" # 告诉 Nikto 不要输出颜色 result = subprocess.run(cmd, env=env, ...)4.5 现象:Django Admin 登录后,ScanTask列表页报错FieldError: Unsupported lookup 'icontains' for JSONField,点击某条记录进入详情页崩溃
原因:models.JSONField()在 Django 3.2 中不支持icontains查询(如ScanTask.objects.filter(report_data__icontains="SQL")),但某些 Admin 配置或模板里误用了。
解决:
① 检查admin.py,移除对report_data字段的search_fields或list_filter;
② 若需搜索,改用Vulnerability模型:Vulnerability.objects.filter(description__icontains="SQL");
③ 在 Admin 的list_display中,用方法代替字段:
@admin.register(ScanTask) class ScanTaskAdmin(admin.ModelAdmin): list_display = ['target_url', 'start_time', 'status', 'vuln_count'] def vuln_count(self, obj): return obj.vulnerability_set.count() # 关联模型计数 vuln_count.short_description = 'Vulns'5. 让扫描器真正可用:添加自定义检测模块与结果可视化技巧
跑通只是起点,课程设计/毕业设计要体现你的工程能力:能不能加一条自己的检测规则?能不能把枯燥的 JSON 列表变成带风险等级色块的仪表盘?能不能导出 PDF 报告?这些不是炫技,是证明你理解了“系统”的边界在哪里、扩展点在哪里。
5.1 添加一个轻量级 XSS 检测模块:绕过 Nikto 的局限
Nikto 不检测 XSS,但我们可以用 Python 补上。思路:对目标 URL 的每个可提交表单(<form>),用requests发送<script>alert(1)</script>到所有input[type=text]字段,检查响应体是否原样回显。这比写完整爬虫简单,又比“只扫/xss-test/”更实用。
# program/xss_detector.py import requests from bs4 import BeautifulSoup from urllib.parse import urljoin, urlparse def detect_xss_on_form(target_url, timeout=10): """ 对目标 URL 的第一个表单进行基础 XSS 测试 :return: list of dict, e.g., [{"url": "https://a.com/login", "field": "username", "payload": "<script>"}] """ try: resp = requests.get(target_url, timeout=timeout) resp.raise_for_status() except Exception: return [] soup = BeautifulSoup(resp.text, 'html.parser') forms = soup.find_all('form', limit=1) if not forms: return [] form = forms[0] action = form.get('action') or target_url full_action = urljoin(target_url, action) # 提取所有 text 类型 input inputs = form.find_all('input', {'type': 'text'}) results = [] for inp in inputs: name = inp.get('name') if not name: continue # 构造 payload 并 POST data = {name: '<script>alert(1)</script>'} try: post_resp = requests.post(full_action, data=data, timeout=timeout) if '<script>alert(1)</script>' in post_resp.text: results.append({ "url": full_action, "field": name, "payload": "<script>alert(1)</script>", "evidence": "Payload reflected in response" }) except Exception: pass return results然后在views.py的扫描视图中调用:
# vuln_scan/views.py from program.xss_detector import detect_xss_on_form def start_scan(request): if request.method == 'POST': target = request.POST.get('target_url') # ... 创建 ScanTask ... # 并行执行 Nikto 和 XSS 检测 nikto_result = run_nikto(target) xss_result = detect_xss_on_form(target) # 合并结果(示例:追加到 vulnerabilities 数组) if "vulnerabilities" not in nikto_result: nikto_result["vulnerabilities"] = [] for xss in xss_result: nikto_result["vulnerabilities"].append({ "id": "XSS_CUSTOM_001", "msg": f"XSS possible in field '{xss['field']}'", "uri": xss["url"], "method": "POST", "severity": "2" # HIGH }) # 保存合并后结果 scan_task.report_data = nikto_result scan_task.status = "COMPLETED" scan_task.save() parse_nikto_result(nikto_result, scan_task)为什么有效:这个模块不依赖外部工具,纯 Python 实现,代码少、易懂、可调试。它展示了你对“检测逻辑”的抽象能力——不是堆功能,而是识别缺口、精准补位。
5.2 用 ECharts 实现动态风险仪表盘:三步搞定前端可视化
项目自带layui.css,说明它倾向轻量前端框架。ECharts 是最匹配的选择:体积小、文档全、中文好。只需三步:
①在 HTML 模板中引入 ECharts(templates/scan_result.html):
<!-- 在 </body> 前 --> <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script> <div id="chart" style="width: 100%; height: 400px;"></div>②用 Django 视图传递结构化数据(views.py):
def scan_result(request, task_id): task = get_object_or_404(ScanTask, id=task_id) # 从 JSON 中提取 severity 分布 vulns = task.report_data.get("vulnerabilities", []) severity_count = {"LOW":0, "MEDIUM":0, "HIGH":0, "CRITICAL":0} for v in vulns: sev = v.get("severity", "LOW") severity_count[sev] = severity_count.get(sev, 0) + 1 context = { 'task': task, 'severity_data': list(severity_count.items()), # [('LOW', 12), ('MEDIUM', 5), ...] } return render(request, 'scan_result.html', context)③前端渲染图表(<script>块内):
const chartDom = document.getElementById('chart'); const myChart = echarts.init(chartDom); const option = { title: { text: '漏洞风险分布' }, tooltip: {}, legend: { data: ['低危', '中危', '高危', '严重'] }, series: [{ name: '数量', type: 'pie', radius: '55%', data: [ { value: {{ severity_data.0.1 }}, name: '低危' }, { value: {{ severity_data.1.1 }}, name: '中危' }, { value: {{ severity_data.2.1 }}, name: '高危' }, { value: {{ severity_data.3.1 }}, name: '严重' } ] }] }; myChart.setOption(option);效果:一个彩色饼图,直观展示各风险等级占比。导师一眼看懂“你不仅扫了,还做了分析”。这比堆砌 100 行漏洞列表高级得多。
5.3 导出 PDF 报告:用 WeasyPrint 替代难搞的 wkhtmltopdf
很多同学想导 PDF,一搜就撞上wkhtmltopdf——Windows 下 DLL 依赖地狱,Linux 下字体渲染错乱。WeasyPrint 是纯 Python 方案,基于 CSS,完美兼容bootstrap.css。
pip install weasyprint# utils/pdf_export.py from weasyprint import HTML from django.template.loader import render_to_string def generate_pdf_report(task_id): task = ScanTask.objects.get(id=task_id) html_string = render_to_string('pdf_template.html', {'task': task}) html = HTML(string=html_string) return html.write_pdf() # 返回 bytes,可直接 HttpResponse # views.py 中 def export_pdf(request, task_id): pdf = generate_pdf_report(task_id) response = HttpResponse(pdf, content_type='application/pdf') response['Content-Disposition'] = f'attachment; filename="scan_report_{task_id}.pdf"' return responsepdf_template.html只需用 Bootstrap class 控制样式,WeasyPrint 会原样渲染。
从那以后我每次交付课程设计,都强制走一遍python manage.py check --deploy(检查生产配置)、python manage.py showmigrations(确认迁移干净)、python manage.py runserver --noreload(模拟无热重载环境),再用curl -I http://127.0.0.1:8000/看 Header 是否含X-Frame-Options(Django 自动加的安全头)。这些动作不花 2 分钟,却能避开 90% 的“答辩现场崩盘”。希望帮到你。
本文还有配套的精品资源,点击获取