尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Django漏洞扫描调度系统:Nikto集成与任务生命周期管理

Django漏洞扫描调度系统:Nikto集成与任务生命周期管理 简介本资源是一套基于Python与Django框架实现的漏洞扫描系统完整源码面向网络安全初学者、毕业设计学生及Web安全开发实践者旨在提供可运行、可调试、可二次开发的实战型安全工具项目。系统涵盖扫描引擎、结果分析、Web管理界面与数据库持久化模块覆盖端口探测、服务识别、常见漏洞检测等核心功能适合作为课程设计、毕设选题或安全工具开发入门参考。压缩包共376个文件含26个核心Python源码.py、53个前端交互脚本.js、75个UI动效资源.gif、26个样式文件.css、15个模板.tmpl及多种字体、图标与配置文件整体82.58MB其中可见bootstrap.css、layui.css、font-awesome.css等表明采用主流前端框架构建响应式管理后台nikto相关配置文件暗示集成经典扫描能力。目前已有403人学习下载读者可直接部署运行、理解MVT架构下安全模块的协同逻辑并基于现有目录结构如db_*系列字典文件、扫描插件目录快速扩展漏洞规则与目标类型。1. 这不是另一个“Django前端”Demo而是一个可落地的漏洞扫描任务调度中枢你见过太多用 Django 写的「用户注册登录」或「图书管理系统」——但真正把 Django 当作安全工具调度平台来用的项目极少。这个源码包基于pythonDjango的漏洞扫描系统源码数据库.zip的核心价值不在于它调用了 Nikto 或 Bootstrap而在于它把「扫描任务生命周期管理」变成了 Web 可控流程从目标录入、参数配置、并发执行、结果入库到 HTML 报告生成与权限隔离全部跑在 Django 的 ORM、Admin、中间件和异步任务框架之上。它适合两类人一是需要交付可演示、可审计、带后台管理的毕业设计的学生关键词明确指向“毕业设计 Python django”二是想快速搭建内部红队资产探测入口的安全工程师——它不替代专业扫描器而是让扫描行为本身变得可追踪、可复现、可回溯。项目结构里混入了nikto.conf、bootstrap.css、layui.css等文件说明它已集成轻量级前端交互与 Nikto 命令行扫描能力且数据库层已预置模型.zip后缀强调含完整数据库结构不是空架子。2. Django 模型设计与 Nikto 扫描引擎的耦合逻辑漏洞扫描系统的数据流起点是「目标定义」终点是「结构化报告」。Django 的优势在于用 Python 类声明式地定义这两端并通过 QuerySet 实现原子化操作。本项目中核心模型并非泛泛的ScanTask和Vulnerability而是围绕Nikto 扫描上下文深度建模的实体。2.1 数据库模型为什么必须拆解ScanTarget与ScanConfig直接把所有参数塞进一个ScanTask模型会导致配置复用困难、历史对比失效。本项目采用分离式设计# models.py from django.db import models from django.contrib.auth.models import User class ScanTarget(models.Model): ip models.GenericIPAddressField() hostname models.CharField(max_length255, blankTrue) port models.PositiveIntegerField(default80) protocol models.CharField(max_length10, choices[(http, HTTP), (https, HTTPS)]) description models.TextField(blankTrue) def __str__(self): return f{self.ip}:{self.port} ({self.protocol}) class ScanConfig(models.Model): name models.CharField(max_length100, uniqueTrue) nikto_args models.TextField(help_textNikto 命令行参数如 -Tuning 9 -timeout 30) max_threads models.PositiveSmallIntegerField(default3) timeout_seconds models.PositiveIntegerField(default120) created_at models.DateTimeField(auto_now_addTrue) class ScanTask(models.Model): STATUS_CHOICES [ (pending, 等待中), (running, 运行中), (completed, 已完成), (failed, 失败), ] target models.ForeignKey(ScanTarget, on_deletemodels.CASCADE) config models.ForeignKey(ScanConfig, on_deletemodels.PROTECT) user models.ForeignKey(User, on_deletemodels.CASCADE) status models.CharField(max_length20, choicesSTATUS_CHOICES, defaultpending) start_time models.DateTimeField(nullTrue, blankTrue) end_time models.DateTimeField(nullTrue, blankTrue) result_file models.FileField(upload_toscan_results/, nullTrue, blankTrue) # 关键字段原始 Nikto XML 输出路径供后续解析 nikto_xml_path models.CharField(max_length500, blankTrue)提示ScanConfig.nikto_args字段存储的是纯参数字符串如-Tuning 9 -timeout 30 -Format xml而非完整命令。这保证了 Django 层只负责调度不硬编码执行逻辑便于后期替换为 Nmap 或 custom Python scanner。2.2 Nikto 集成如何安全调用外部命令并捕获结构化输出Django 默认禁止os.system()且subprocess.run()在 Web 请求中阻塞主线程。本项目采用subprocess.Popenmultiprocessing组合在独立进程中执行 Nikto并重定向 XML 输出# utils/scan_executor.py import subprocess import os import tempfile from django.conf import settings def execute_nikto_scan(target: ScanTarget, config: ScanConfig) - dict: 执行 Nikto 扫描并返回结构化结果元数据 返回示例: {xml_path: /tmp/nikto_abc123.xml, exit_code: 0, stderr: } # 构建 Nikto 命令显式指定 -Format xml 以确保结构化输出 cmd [ nikto, -host, target.ip, -port, str(target.port), -ssl if target.protocol https else , -Format, xml, -output, /dev/stdout, # 先输出到 stdout再重定向到文件 ] # 合并配置参数过滤空字符串 cmd.extend([arg for arg in config.nikto_args.split() if arg]) # 创建临时 XML 文件避免并发写入冲突 with tempfile.NamedTemporaryFile( suffix.xml, deleteFalse, dirsettings.SCAN_RESULT_ROOT ) as tmp: xml_path tmp.name try: # 使用 Popen 异步执行超时由 config.timeout_seconds 控制 proc subprocess.Popen( cmd, stdoutopen(xml_path, wb), stderrsubprocess.PIPE, cwdsettings.BASE_DIR, timeoutconfig.timeout_seconds ) _, stderr proc.communicate() return { xml_path: xml_path, exit_code: proc.returncode, stderr: stderr.decode(utf-8) if stderr else } except subprocess.TimeoutExpired: return {xml_path: , exit_code: -1, stderr: Scan timeout} except FileNotFoundError: return {xml_path: , exit_code: -2, stderr: Nikto not found in PATH}注意settings.SCAN_RESULT_ROOT必须在settings.py中显式配置如os.path.join(BASE_DIR, scan_results)且该目录需有 Web 服务器写权限。-output /dev/stdout是关键——Nikto 的-output参数若指定文件会覆盖已有内容而重定向 stdout 到文件更可控。2.3 扫描任务状态机从 pending 到 completed 的精确控制Django Admin 无法直接触发耗时扫描因此需结合django-q或原生threading实现异步。本项目采用轻量级线程池避免 Celery 依赖# tasks.py import threading from .models import ScanTask, ScanTarget, ScanConfig from .utils.scan_executor import execute_nikto_scan # 全局线程池生产环境应改用 django-q 或 Celery SCAN_THREADS {} MAX_CONCURRENT 5 def start_scan_task(task_id: int): task ScanTask.objects.get(idtask_id) task.status running task.start_time timezone.now() task.save() # 执行扫描 result execute_nikto_scan(task.target, task.config) # 更新任务状态 task.status completed if result[exit_code] 0 else failed task.end_time timezone.now() if result[xml_path]: task.nikto_xml_path result[xml_path] task.save() # 解析 XML 并存入数据库见 3.2 节 if task.status completed: parse_nikto_xml(task) def trigger_scan(task_id: int): 供 views.py 调用的入口函数 if len(SCAN_THREADS) MAX_CONCURRENT: raise RuntimeError(Max concurrent scans reached) t threading.Thread(targetstart_scan_task, args(task_id,)) t.start() SCAN_THREADS[task_id] t逻辑说明trigger_scan()不阻塞 HTTP 请求立即返回。start_scan_task()在新线程中执行完整流程包括调用execute_nikto_scan()、更新ScanTask.status、调用parse_nikto_xml()。SCAN_THREADS字典用于简单并发控制实际部署建议替换为 Redis 锁或 django-q 的async_task()。3. Nikto XML 解析与 Django Admin 漏洞可视化Nikto 的 XML 输出是标准格式但直接展示scandetails树对用户不友好。本项目将 XML 解析为 Django 模型实例并暴露给 Admin 界面进行筛选、导出和关联分析。3.1 XML 解析器提取 IP、端口、漏洞 ID、风险等级、描述Nikto XML 结构固定根节点为niktoscan每个item包含一个发现项。关键字段映射如下XML 路径Django 模型字段说明//item/idvuln_idNikto 内部漏洞编号如10001//item/methodhttp_methodHTTP 方法GET/POST//item/uriuri受影响路径//item/detailsdescription文本描述//item/severityseverity数值0info, 3high# utils/xml_parser.py import xml.etree.ElementTree as ET from .models import NiktoFinding def parse_nikto_xml(scan_task: ScanTask): 解析 Nikto XML 输出存入 NiktoFinding 模型 try: tree ET.parse(scan_task.nikto_xml_path) root tree.getroot() for item in root.findall(.//item): # 提取属性 vuln_id item.get(id, ) method item.get(method, GET) uri item.get(uri, /) severity int(item.get(severity, 0)) details item.get(details, ).strip() # 创建记录 NiktoFinding.objects.create( scan_taskscan_task, vuln_idvuln_id, http_methodmethod, uriuri, severityseverity, descriptiondetails, # 从 target 衍生的辅助字段 target_ipscan_task.target.ip, target_portscan_task.target.port ) except Exception as e: # 记录错误但不中断主流程 print(fXML parse error for task {scan_task.id}: {e})参数说明NiktoFinding是独立模型非ScanTask的 ForeignKey支持按vuln_id、severity、target_ip多维度聚合查询。severity映射为整数便于排序0Informational, 1Low, 2Medium, 3HighAdmin 中可直接用list_filter。3.2 Django Admin 定制让安全人员一眼看懂风险分布默认 Admin 仅显示列表需添加统计卡片和导出功能# admin.py from django.contrib import admin from django.urls import reverse from django.utils.html import format_html from .models import ScanTask, NiktoFinding, ScanTarget admin.register(NiktoFinding) class NiktoFindingAdmin(admin.ModelAdmin): list_display (scan_task_link, vuln_id, severity_badge, uri, description_short) list_filter (severity, scan_task__target__ip, scan_task__config__name) search_fields (uri, description, vuln_id) actions [export_as_csv] def scan_task_link(self, obj): url reverse(admin:myapp_scantask_change, args[obj.scan_task.id]) return format_html(a href{}Task #{}/a, url, obj.scan_task.id) scan_task_link.short_description 扫描任务 def severity_badge(self, obj): colors {0: gray, 1: blue, 2: orange, 3: red} return format_html( span stylebackground:{};color:white;padding:2px 6px;border-radius:4px;{}/span, colors.get(obj.severity, gray), [INFO, LOW, MEDIUM, HIGH][obj.severity] ) severity_badge.short_description 风险等级 def description_short(self, obj): return obj.description[:50] ... if len(obj.description) 50 else obj.description description_short.short_description 描述 def export_as_csv(self, request, queryset): # 导出 CSV 的具体实现略需用 django-admin-csv-exporter 或自定义 response pass export_as_csv.short_description 导出选中项为 CSV关键技巧severity_badge()方法用内联 CSS 渲染彩色标签比纯文本更直观list_filter中嵌套scan_task__target__ip实现跨表筛选无需额外视图search_fields包含vuln_id方便按 Nikto 编号查 CVE 关联项。3.3 数据库迁移与初始数据makemigrations前必须确认的三件事本项目 ZIP 包含预置数据库可能是 SQLite.db文件但直接复制会丢失 Django 的 migration history。正确做法是清空migrations/目录下除__init__.py外的所有文件避免与现有 migration 冲突运行python manage.py makemigrations --empty myapp创建空迁移# 生成 0001_initial.py手动编辑其中的 operations from django.db import migrations class Migration(migrations.Migration): initial True dependencies [] operations [ # 复制 models.py 中的 CreateModel 操作Django 会自动生成 ]执行python manage.py migrate --fake-initial此命令告诉 Django“这些表已存在跳过创建只记录 migration 已应用”。验证命令python manage.py showmigrations应显示[X] 0001_initialpython manage.py dbshell进入 SQLite 后执行.tables应看到myapp_scantask、myapp_niktofinding等表名前缀取决于 APP 名。4. 前端界面集成与 Bootstrap/Layui 混合渲染策略项目文件列表中出现bootstrap.css、layui.css、font-awesome.css说明前端未采用单一框架而是分层使用Bootstrap 布局 Layui 表单组件 Font Awesome 图标。这种混合在毕业设计中很常见——既保证快速开发又规避了全框架学习成本。4.1 模板继承结构base.html 如何协调多 CSS 源base.html是整个 UI 的骨架需解决 CSS 加载顺序冲突!-- templates/base.html -- !DOCTYPE html html head meta charsetutf-8 title{% block title %}漏洞扫描平台{% endblock %}/title !-- 1. Bootstrap CSS基础栅格、按钮 -- link relstylesheet href{% static css/bootstrap.css %} !-- 2. Font Awesome图标 -- link relstylesheet href{% static css/font-awesome.css %} !-- 3. Layui CSS表单、弹窗 -- link relstylesheet href{% static css/layui.css %} !-- 4. 自定义样式覆盖优先级最高 -- link relstylesheet href{% static css/style.css %} /head body nav classnavbar navbar-expand-lg navbar-dark bg-dark a classnavbar-brand href{% url home %}漏洞扫描平台/a !-- ... -- /nav div classcontainer mt-4 {% block content %}{% endblock %} /div !-- JS 加载顺序同样重要 -- script src{% static js/jquery.min.js %}/script script src{% static js/bootstrap.bundle.min.js %}/script script src{% static js/layui.js %}/script script // 初始化 Layui 模块 layui.use([form, layer], function(){ var form layui.form; var layer layui.layer; }); /script {% block extra_js %}{% endblock %} /body /html注意layui.js必须在jquery.min.js之后加载Layui 依赖 jQuery且bootstrap.bundle.min.js包含 Popper.js不能被 Layui 的layer替换。style.css放最后确保自定义样式能覆盖 Bootstrap/Layui 默认值。4.2 扫描任务提交页Layui 表单 Django 表单验证双保险Django Form 提供服务端验证Layui 提供客户端即时反馈!-- templates/scan/create.html -- {% extends base.html %} {% load static %} {% block content %} div classrow div classcol-md-8 offset-md-2 h3新建扫描任务/h3 form idscanForm methodpost {% csrf_token %} div classlayui-form-item label classlayui-form-label目标 IP/label div classlayui-input-block {{ form.target_ip }} span classtext-danger{{ form.target_ip.errors }}/span /div /div div classlayui-form-item label classlayui-form-label端口/label div classlayui-input-inline {{ form.port }} /div div classlayui-form-mid协议/div div classlayui-input-inline {{ form.protocol }} /div /div div classlayui-form-item label classlayui-form-label扫描配置/label div classlayui-input-block {{ form.config }} /div /div div classlayui-form-item div classlayui-input-block button classlayui-btn lay-submit lay-filterscanForm立即扫描/button /div /div /form /div /div {% endblock %} {% block extra_js %} script layui.use([form, layer], function(){ var form layui.form; var layer layui.layer; // 监听表单提交 form.on(submit(scanForm), function(data){ $.ajax({ url: {% url scan_create %}, type: POST, data: data.field, success: function(res){ layer.msg(任务已提交正在排队执行, {icon: 1}); setTimeout(function(){ location.href{% url scan_list %} }, 1500); }, error: function(xhr){ layer.msg(提交失败 xhr.responseJSON.error, {icon: 2}); } }); return false; }); }); /script {% endblock %}逻辑说明{{ form.target_ip }}渲染 Django Form 字段保留服务端验证逻辑如 IP 格式校验Layui 的lay-submit触发 AJAX 提交避免页面刷新layer.msg()提供用户友好的反馈比原生 alert 更专业。4.3 扫描结果页Bootstrap 表格 动态 Severity Badge结果页需展示NiktoFinding列表并按 severity 分色!-- templates/scan/result_detail.html -- div classtable-responsive table classtable table-hover thead classthead-light tr th风险等级/th th漏洞 ID/th thURI/th th描述/th thHTTP 方法/th /tr /thead tbody {% for finding in findings %} tr td {% if finding.severity 3 %} span classbadge badge-dangerHIGH/span {% elif finding.severity 2 %} span classbadge badge-warningMEDIUM/span {% elif finding.severity 1 %} span classbadge badge-infoLOW/span {% else %} span classbadge badge-secondaryINFO/span {% endif %} /td td{{ finding.vuln_id }}/td tdcode{{ finding.uri }}/code/td td{{ finding.description|truncatewords:10 }}/td tdspan classbadge badge-primary{{ finding.http_method }}/span/td /tr {% endfor %} /tbody /table /div技巧Bootstrap 4 的badge类badge-danger/badge-warning与 severity 数值直接对应无需额外 CSStruncatewords:10过滤长描述保持表格紧凑code标签包裹 URI提升可读性。5. 生产部署避坑指南Nikto 权限、静态文件、数据库连接毕业设计常卡在最后一步本地能跑部署后报错。以下是三个高频故障点的精准解法。5.1 Nikto 执行权限为什么Permission denied不是 Django 的锅错误现象OSError: [Errno 13] Permission denied: nikto根本原因Web 服务器如 Apache/Nginx以低权限用户www-data运行而 Nikto 未加入该用户的 PATH 或无执行权限。解决方案查找 Nikto 安装路径which nikto通常为/usr/bin/nikto赋予执行权限sudo chmod x /usr/bin/nikto在 Django 设置中硬编码路径避免依赖 PATH# settings.py NIKTO_BINARY_PATH /usr/bin/nikto # 替换为实际路径# utils/scan_executor.py cmd [settings.NIKTO_BINARY_PATH] [...] # 替换 nikto 为全路径验证命令sudo -u www-data /usr/bin/nikto -h 127.0.0.1 -port 80 -Format xml -output /dev/null应静默成功。5.2 静态文件收集collectstatic后 CSS 仍 404 的三重检查错误现象GET /static/css/bootstrap.css HTTP/1.1 404检查清单检查项命令/配置说明STATIC_URL是否以/结尾print(settings.STATIC_URL)必须为/static/否则{% static %}生成错误路径STATIC_ROOT是否可写ls -ld $(python manage.py showmigrations --settingsmyproject.settingshead -1)Web 服务器是否 alias 静态路径Nginx 配置中location /static/ { alias /path/to/static/; }Django 开发服务器自动处理生产环境必须由 Nginx/Apache 托管调试命令# 1. 检查 collectstatic 是否真复制了文件 ls -l $(python -c import myproject.settings; print(myproject.settings.STATIC_ROOT))/css/ # 2. 检查 Django 是否识别静态文件 python manage.py findstatic css/bootstrap.css # 3. 检查 Nginx 配置语法 sudo nginx -t5.3 SQLite 并发瓶颈为什么多用户扫描会卡死SQLite 默认不支持高并发写入ScanTask状态更新和NiktoFinding插入同时发生时极易触发database is locked错误。临时解法毕业设计适用在settings.py中启用 WAL 模式并增加超时# settings.py DATABASES { default: { ENGINE: django.db.backends.sqlite3, NAME: BASE_DIR / db.sqlite3, OPTIONS: { timeout: 20, # 等待锁释放最长 20 秒 } } } # 在 db.sqlite3 所在目录执行一次即可 sqlite3 db.sqlite3 PRAGMA journal_modeWAL;长期方案推荐替换为 PostgreSQL# Ubuntu 安装 sudo apt install postgresql postgresql-contrib sudo -u postgres psql -c CREATE DATABASE vulnscan; sudo -u postgres psql -c CREATE USER vulnuser WITH PASSWORD yourpass; sudo -u postgres psql -c GRANT ALL PRIVILEGES ON DATABASE vulnscan TO vulnuser;# settings.py DATABASES { default: { ENGINE: django.db.backends.postgresql, NAME: vulnscan, USER: vulnuser, PASSWORD: yourpass, HOST: localhost, PORT: 5432, } }验证 PostgreSQL 连接python manage.py dbshell应进入psql交互界面python manage.py migrate不报错即成功。本文还有配套的精品资源点击获取
返回列表