DipesThapa/PortScanner
GitHub: DipesThapa/PortScanner
基于 Nmap 的安全工程化扫描平台,提供 Python CLI 引擎、FastAPI 服务与 React 前端,支持插件扩展、基线差异报告和 SIEM 就绪的 JSON 输出。
Stars: 1 | Forks: 0
# PortScanner — 网络侦察与漏洞编排平台
[](https://github.com/DipesThapa/PortScanner/actions/workflows/ci.yml)
[](https://github.com/DipesThapa/PortScanner/actions/workflows/codeql.yml)
[](https://www.python.org/)
[](#license)
一个安全工程化的平台,将 **Nmap** 转变为完整的扫描服务:一个模块化的 Python 引擎,一个用于威胁情报/自动化响应/深度分析工具的插件系统,历史基线记录,以及一个经过认证的 FastAPI + React Web 应用程序,用于启动和审查扫描。
📝 **撰写:** [*加固我自己的 Nmap Web UI:我发布的安全漏洞,以及真正拯救了我的东西*](https://dev.to/dipesthapa/i-shipped-an-unauthenticated-rce-in-my-own-port-scanner-heres-the-whole-chain-and-how-i-killed-35kl)
(dev.to) · [源码](docs/writeup-rce-to-hardened.md)
## 为什么做这个项目
大多数“端口扫描器”项目只是将 `nmap` 包装在一个 subprocess 调用中然后就止步了。
这个项目的构建就像是一个安全团队真正可以部署的产品:
- **设计上的纵深防御** — Web 层经过身份验证,每个扫描输入都经过验证,命令执行被列入白名单,并且密钥在日志中被脱敏。[威胁模型](#threat-model) 是文档化的,而不是隐含的。
- **可扩展引擎** — 插件、导出器、凭据提供者和分布式工作程序抽象是一等公民,而不是后期附加的。
- **可操作性** — 容器化(非 root)、健康检查、CI 测试,并且在每次推送时都由 CodeQL、Bandit 和 pip-audit 进行扫描。
## 架构
```
flowchart LR
subgraph Client
UI[React + Vite dashboard]
end
subgraph Web tier - FastAPI
AUTH[API-key auth\nX-API-Key / ws token]
API[REST + WebSocket API]
JOBS[JobManager\nbackground tasks]
DD[DeepDiveExecutor\nallowlisted, shell=False]
end
subgraph Engine - portscanner package
CLI[CLI / orchestrator]
SCAN[Nmap runner\nvalidated argv + -- sentinel]
PARSE[XML parser]
PLUG[Plugins: threat-intel,\nauto-responder, deep-dive]
BASE[Baseline store + diff]
end
DB[(SQLite\nscans, schedules, workers)]
NMAP[[nmap binary\ncap_net_raw only]]
UI -->|X-API-Key| AUTH --> API
API --> JOBS --> CLI --> SCAN --> NMAP
CLI --> PARSE --> PLUG --> BASE
JOBS --> DB
API --> DD --> NMAP
API <-->|/ws/status| UI
```
**请求生命周期:** UI 提交扫描(`POST /api/scans`)并附带 API 密钥 →
`JobManager` 验证输入,持久化记录,并在后台线程中运行 CLI 引擎 → 引擎构建安全的 `nmap` argv,解析 XML,运行插件,并写入产出物 + 基线 → UI 通过 `/ws/status` 流式传输状态,并在完成后下载产出物。
## 组件映射
| 层级 | 路径 | 职责 |
|-------|------|----------------|
| Web API | `webapp/main.py` | 路由、认证装配、SPA 服务 |
| Auth | `webapp/security.py` | API 密钥生成、验证、WebSocket 认证 |
| Jobs | `webapp/jobs.py` | 扫描生命周期、产出物收集、日志脱敏 |
| Deep-dive | `webapp/deepdive.py` | 白名单内的后续命令执行 |
| Models | `webapp/models.py` | Pydantic 请求/响应 + 输入验证 |
| Engine | `portscanner/` | Nmap 运行器、解析器、插件、基线、导出器 |
| Frontend | `webapp/frontend/` | React + Vite 仪表板 |
## 安全模型
完整细节请见 [SECURITY.md](SECURITY.md)。亮点:
- **到处都是身份验证。** 每个 `/api/*` 路由和 `/ws/status` WebSocket 都需要 `X-API-Key`。密钥来自 `PORTSCANNER_API_KEY` 或者在首次启动时生成到 `web_runs/.api_key`(模式 `0600`)— 该服务从不匿名运行。`GET /api/health` 特意为健康检查而开放。
- **输入验证。** 目标、端口、脚本名称和额外的 nmap 参数都经过严格验证。类似标志的目标和危险标志(`--script`、`-oN`、`--datadir`、…)会被拒绝,并且一个 `--` 哨兵会在目标之前终止 nmap 选项解析(针对参数注入的纵深防御)。
- **命令执行被列入白名单**,并使用 `shell=False` 运行。上传的脚本不能自行授权;除非设置 `PORTSCANNER_ENABLE_SCRIPT_UPLOAD=1`,否则脚本上传是被禁用的。
- **密钥清理。** 密码、token、bearer header 和 `secret://` 引用在扫描日志持久化之前会被脱敏。
- **加固的容器。** 以非 root 用户运行;`cap_net_raw` 仅授予 nmap 二进制文件,因此无需以 root 身份运行服务即可进行 raw 扫描。
### 威胁模型
| # | 威胁 | 向量 | 缓解措施 |
|---|--------|--------|------------|
| T1 | 未经身份验证的访问 | 暴露的 API / WebSocket | 所有 `/api` 路由 + WS 上的 API 密钥认证;常数时间比较 |
| T2 | 远程命令 / NSE 执行 | 通过目标/参数注入 `--script=…` | 目标和参数验证,危险标志黑名单,`--` 哨兵 |
| T3 | 通过脚本上传执行任意代码 | `POST /api/plugins/scripts` | 默认禁用;上传的内容永远不会自动加入白名单 |
| T4 | 深度分析中的命令注入 | 伪造的命令字符串 | 显式白名单 + `shell=False` + `shlex` 分词 |
| T5 | 密钥泄露 | 扫描日志持久化到数据库 | 持久化前进行脱敏处理;`.api_key`/凭据被 gitignored |
| T6 | 权限提升 | 服务以 root 身份运行 | 非 root 容器;能力范围仅限于 nmap 二进制文件 |
| T7 | 供应链漂移 | 未锁定的依赖项 | 锁定 `requirements.txt`;CI 中的 pip-audit + CodeQL |
| T8 | 扫描滥用 / SSRF 式侦察 | 未受限制的目标 | 认证门控;部署在具有源 IP 白名单的代理之后(参见 SECURITY.md) |
## 快速开始
### Docker(推荐)
```
docker compose up --build
# API + UI 在 http://localhost:8000
# 首次启动时,生成的 API key 会打印到日志并存储在 web_runs/.api_key
```
设置您自己的密钥而不是生成的密钥:
```
PORTSCANNER_API_KEY=$(openssl rand -base64 32) docker compose up --build
```
### 本地开发
```
# Backend
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
export PORTSCANNER_API_KEY=$(openssl rand -base64 32)
uvicorn webapp.main:app --reload # http://127.0.0.1:8000
# Frontend(单独的终端)
cd webapp/frontend
npm install
npm run dev # http://127.0.0.1:5173 (proxies /api to :8000)
```
UI 在首次加载时会提示输入 API 密钥,并将其存储在 `localStorage` 中。
### 经过身份验证的 API 示例
```
KEY=$(cat web_runs/.api_key) # or your PORTSCANNER_API_KEY
curl -s http://localhost:8000/api/health # open
curl -s -H "X-API-Key: $KEY" http://localhost:8000/api/scans # authenticated
curl -s -H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{"target":"scanme.nmap.org","ports":"1-1024","intel":true}' \
http://localhost:8000/api/scans
```
### Web API 端点
| 方法 | 路径 | 描述 |
|--------|------|-------------|
| `GET` | `/api/health` | 存活检查(未经身份验证) |
| `POST` | `/api/scans` | 提交扫描(与 CLI 选项相同) |
| `GET` | `/api/scans` | 列出任务及其状态、摘要、插件输出 |
| `GET` | `/api/scans/{id}` | 检查任务:日志、差异、产出物 |
| `GET` | `/api/scans/{id}/artifacts/{path}` | 下载生成的产出物 |
| `POST` | `/api/scans/{id}/deepdive` | 运行白名单内的后续命令 |
| `WS` | `/ws/status?token=KEY` | 实时任务/worker/深度分析状态流 |
## CLI 引擎
相同的引擎可以在没有 Web 层的情况下独立运行。
```
python3 port_scanner.py --target example.com --ports 1-1024 --intel
```
### 关键输出与选项标志
- `--save-xml PATH` / `--save-vulns PATH` / `--save-report PATH` / `--output-json PATH`
- `--output-dir DIR` — 针对特定目标的 `*.xml` 和 `*.vulns.json`
- `--intel [--intel-scripts …]` — 服务信息丰富 + 补救提示
- `--baseline-json PATH` / `--diff-report PATH` / `--baseline-store DIR`
- `--plugins … [--plugin-config PATH]`、`--exporters … [--exporter-config PATH]`
- `--credential-file PATH` / `--secret-file PATH` / `--secret-prefix PREFIX`
- `--asset-file PATH`、`--orchestrator-config PATH`、`--job-file PATH`
运行 `python3 port_scanner.py --help` 获取完整列表。
## 测试与质量门禁
```
export PORTSCANNER_API_KEY=test-key-0123456789
pytest -q
```
CI(`.github/workflows/`)在每次推送和 PR 时运行:
- **`ci.yml`** — pytest(Python 3.11 & 3.12)、**Bandit** 静态安全扫描、**pip-audit** 依赖项漏洞检查,以及前端构建。
- **`codeql.yml`** — 针对 Python 和 JavaScript/TypeScript 的 GitHub CodeQL 分析,每周及在 PR 时运行。
## 配置参考
| 变量 | 用途 | 默认值 |
|----------|---------|---------|
| `PORTSCANNER_API_KEY` | API 密钥(16+ 个字符) | 自动生成到 `web_runs/.api_key` |
| `PORTSCANNER_ENABLE_SCRIPT_UPLOAD` | 允许 `POST /api/plugins/scripts` | 禁用 |
| `PORTSCANNER_SAFE_CONFIG` | 安全扫描配置的路径 | `/config_safe.json` |
| `DEEP_DIVE_ALLOWLIST` | 允许的深度分析命令(CSV 或文件) | `testssl.sh,nmap,nuclei` |
| `DEEP_DIVE_ALLOWLIST_FILE` | 允许命令的 JSON/文本文件 | – |
| `DEEP_DIVE_ALLOW_ALL` | `1` 禁用白名单强制执行(不安全) | 禁用 |
## 项目布局
```
portscanner/ Core engine (scanner, parser, plugins, baselines, exporters)
webapp/ FastAPI backend (main, security, jobs, deepdive, models)
webapp/frontend/ React + Vite dashboard
tests/, webapp/tests/ Pytest suites
.github/workflows/ CI + CodeQL
Dockerfile, docker-compose.yml Container build (non-root)
SECURITY.md Security & threat model
```
## 许可证
MIT — 详见 `LICENSE`(如果不存在请添加)。
更多 CLI 示例(批处理、插件、基线、凭据、配置)
### 批量扫描与并发 ``` python3 port_scanner.py \ --targets 192.168.1.10 192.168.1.11 \ --ports 1-65535 --concurrency 2 \ --output-dir scans/ --output-json combined.json ``` ### 服务情报 ``` python3 port_scanner.py --target web.example.com --intel \ --intel-scripts banner,http-title,ssl-cert ``` ### 插件与自动化 ``` python3 port_scanner.py --target 192.168.1.42 --intel \ --plugins threat-intel auto-responder deep-dive \ --plugin-config plugin-options.json ``` ``` { "auto-responder": {"min_severity": "critical", "forbidden_ports": ["23", "3389"]}, "deep-dive": {"commands": {"http": ["nuclei -target {target}:{port}"]}} } ``` ### 差异报告与基线 ``` python3 port_scanner.py --targets 192.168.1.10 192.168.1.11 \ --baseline-json baselines/latest.json \ --diff-report diffs/today.json --output-json reports/today.json ``` ### 凭据存储 ``` python3 port_scanner.py --target core-switch.local \ --credential-file creds.json --secret-file secrets.json --plugins deep-dive ``` ### 配置文件 ``` python3 port_scanner.py --config scan-config.json ``` ### 使用保存的 XML ``` python3 port_scanner.py --xml-file scan.xml --save-report recap.txt --save-vulns recap.json ```标签:AV绕过, CTI, FastAPI, Python, RESTful API, 提示词优化, 插件系统, 数据统计, 无后门, 端口扫描, 请求拦截, 逆向工具