kayefi737/AegisScan
GitHub: kayefi737/AegisScan
一款基于 FastAPI 的网站外部安全态势被动扫描器,能在十秒内完成九大类 20 余项只读检查并给出透明评级和具体修复方案。
Stars: 0 | Forks: 0
# 🛡️ AegisScan
**在大约十秒钟内评估任何网站的外部安全态势。**
你只需输入一个主机名。AegisScan 会跨越 **九个**类别运行 20 多项被动的、只读的检查,计算出一个透明清晰的 **A+ → F** 评级,并返回一份报告,其中每一个失败项都附带用于修复的*确切配置行*及其 *OWASP Top-10* 映射。无需安装、无需注册、运行它也不需要任何外部服务。
AegisScan 是 **PostureScan**(TechCrush 云计算第 6 期,第 12 组)的强化版本。它保留了 PostureScan 的所有优点(即时评级、具体修复方案、访客扫描、注重隐私的公共仪表板),并将 PostureScan 自己的“未来工作”幻灯片中的几项内容直接推向了产品级:更深度的检查、OWASP 映射,以及一流且有完善文档的 REST API。它还可以通过**单条命令运行,且完全不依赖任何托管服务**,这是原版做不到的。
## 目录
1. [核心想法](#-the-idea)
2. [相较于 PostureScan 的强化之处](#-whats-stronger-than-posturescan)
3. [工作原理(流程)](#-how-it-works-the-flow)
4. [架构](#-architecture)
5. [技术栈](#-tech-stack)
6. [检查项(全部九大类别)](#-the-checks-all-nine-categories)
7. [评分标准](#-scoring-rubric)
8. [快速开始](#-quick-start)
9. [配置](#-configuration)
10. [REST API 参考](#-rest-api-reference)
11. [报告](#-reports)
12. [安全模型与 SSRF 防护](#-security-model--the-ssrf-guard)
13. [测试](#-testing)
14. [项目结构](#-project-structure)
15. [部署与 CI/CD](#-deployment--cicd)
16. [路线图](#-roadmap)
17. [负责任地使用](#-responsible-use)
18. [许可证](#-license)
## 💡 核心想法
关于当今的 Web 安全,有三件事是事实:
1. **几乎每个生产环境网站都至少有一个极其容易修复的配置错误**:缺少安全标头、Cookie 没有设置 `Secure`、域名没有 DMARC 记录。
2. **要找出这些问题通常意味着你需要组装五种专业工具**,并阅读五种不同的规范。
3. **综合扫描器都隐藏在注册门槛和定价页面之后**,而免费工具各自只覆盖一个类别。
所以,尽管每个人都同意应该运行这些检查,但实际上几乎没有人去运行。AegisScan 弥补了这一差距:**一个输入,一份报告,每一项检查,都为你写好了修复方案。**
## 🚀 相较于 PostureScan 的强化之处
| 领域 | PostureScan | **AegisScan** |
|---|---|---|
| 检查类别 | 8 | **9**(增加了专门的深度 CSP 类别 + `.well-known`) |
| 邮件认证 | DMARC | **SPF + DMARC + CAA + DNSSEC** |
| 标头覆盖范围 | 核心集合 | 增加了 **Referrer-Policy, Permissions-Policy, COOP, version-disclosure** |
| OWASP 映射 | 未来工作 | **每一项发现都映射到 OWASP Top-10 2021** ✅ |
| 文档化的 REST API | 未来工作 | **在 `/docs` 自动生成 OpenAPI/Swagger** ✅ |
| 扫描引擎 | 顺序执行 | **异步、并发探测**(`asyncio.gather`) |
| 评分机制 | 显示评级 | **公开、可复现的加权评分标准**(`/api/meta`) |
| 运行环境要求 | React + Django + Supabase + Railway + Vercel | **一条 `uvicorn` 命令,SQLite,零托管服务** |
| 报告 | PDF(需认证) | **JSON + HTML(公开)+ PDF(需认证)** |
| 驱动/运行时 bug | 在 Py 3.13 上遇到 `psycopg2` + Sentry 崩溃 | **内置 `psycopg` v3 + Sentry ≥ 2.61**,预先修复了那些确切的 bug |
| 增加检查项 | 编辑扫描器 | **在检查模块中丢入一个函数**,运行器会自动识别 |
最后三行是刻意为之的:PostureScan 自己的幻灯片 9-10 记录了 `psycopg2`-on-Python-3.13 故障、Sentry 递归崩溃以及 CORS 预览不匹配的问题。AegisScan 锁定了已修复的版本,并使用一条 CORS 正则表达式规则,从而确保这些问题永远不会在此发生。
## 🔄 工作原理(流程)
```
hostname ─▶ normalize ─▶ SSRF guard ─▶ probe (concurrent) ─▶ evaluate ─▶ score ─▶ store ─▶ report
│ │ │ │
│ │ │ └─ weighted A+→F grade
│ │ └─ 20+ pure check functions read the probe
│ └─ HTTPS GET, HTTP GET, TLS handshake, DNS, /.well-known (in parallel)
└─ resolve + reject private / loopback / reserved IPs
```
1. **标准化** 输入:剥离协议、路径、端口、用户信息 → 纯主机名。
2. **防护**:解析它,如果它指向私有、环回、链路本地或保留地址,则拒绝继续(SSRF 防护)。
3. **并发探测一次**:一个 HTTPS 请求(跟随重定向)、一个普通 HTTP 请求(测试重定向到 HTTPS)、一个原始 TLS 握手、一批 DNS 查询,以及一个 `/.well-known/security.txt` 抓取,所有这一切同时并行进行。
4. **评估**:每一项检查都是一个*纯函数*,它读取共享的探测数据并返回结果。没有任何检查会发起自己的网络调用,因此引擎运行极快且在测试中具有确定性。
5. **评分** 根据公开的评分标准 → 评级。
6. **存储** 完整的结构化结果并将其返回。
## 🏗️ 架构
```
┌──────────────────────────────────────────────┐
Browser ───▶ │ FastAPI app (uvicorn) │
(SPA) │ ├─ / served single-page frontend │
│ ├─ /api/* JSON REST API + JWT auth │
│ ├─ /docs auto OpenAPI / Swagger UI │
│ └─ scan engine async, pure check functions │
└───────────────┬──────────────────────────────┘
│ SQLAlchemy
▼
┌────────────────────────┐
│ SQLite (default) │ swap to Postgres
│ or Postgres (optional) │ with one env var
└────────────────────────┘
The scan engine reaches OUT to target sites, strictly read-only, behind an
SSRF guard that blocks private address space.
```
与 PostureScan 的五服务生产拓扑(React/Vercel ×2 + Django/Railway ×2 + Supabase)不同,AegisScan 是 **单进程** 运行的,同时提供 API 和前端服务。这是一个经过深思熟虑的权衡:对于单团队项目来说,运行和推理都变得极其简单;而且由于除了数据库之外它是无状态的,它仍然可以在负载均衡器后进行横向扩展。
## 🧰 技术栈
| 层级 | 选择 | 原因 |
|---|---|---|
| API | **FastAPI**(Python 3.11–3.13) | 异步、强类型、免费的 OpenAPI 文档 |
| 服务器 | **uvicorn** | ASGI,一条命令搞定 |
| ORM / DB | **SQLAlchemy 2** → 默认 **SQLite** / 可选 **Postgres** | 本地零配置,需要时即可投入生产 |
| 认证 | **JWT**(python-jose)+ **bcrypt**(passlib) | 无状态的访客 + 认证流程 |
| 探测 | **httpx**(异步)+ 标准库 `ssl`/`socket` + **dnspython** | 高并发,无重依赖 |
| 报告 | **Jinja2**(HTML)+ **reportlab**(PDF) | reportlab 是纯 Python 实现,不需要系统库 |
| 前端 | **原生 JS + Tailwind (CDN)** | 无构建步骤;整个应用通过 `uvicorn` 运行 |
| 监控 | **sentry-sdk ≥ 2.61**(可选) | 这个版本*不会*在 Python 3.13 上崩溃 |
| 测试 | **pytest** | 完全离线运行 |
| CI | **GitHub Actions**(3.11 / 3.12 / 3.13 矩阵) | 提早发现驱动/运行时的破坏性更改 |
全部开源。全部是标准技术。没有任何非主流的异类。
## 🔍 检查项(全部九大类别)
每一项发现的状态为 `pass` / `warn` / `fail` / `info`,包含严重程度和权重,并且(如果失败)会附带具体的修复方案和 OWASP Top-10 映射。
### 1. TLS & 传输层 (`tls`)
- 站点可通过 HTTPS 访问
- 协商了现代的 TLS 协议(TLS 1.2 / 1.3)
- 证书处于有效期内(过期时间 + 颁发者)
- 普通 HTTP 重定向到 HTTPS
### 2. 安全标头 (`headers`)
- 存在 `Strict-Transport-Security`(HSTS),且 `max-age` 设置合理
- 存在 `X-Content-Type-Options: nosniff`
- 点击劫持防护(`X-Frame-Options` **或** CSP `frame-ancestors`)
- `Referrer-Policy` 设置为保护隐私的值
- 存在 `Permissions-Policy`
- 存在 `Cross-Origin-Opener-Policy`
- 未泄露软件版本号(`Server` / `X-Powered-By`)
### 3. 内容安全策略 (`csp`):*深度分析*
- 存在 CSP 标头
- script 上下文中没有 `'unsafe-inline'`
- 没有 `'unsafe-eval'`
- 定义了 `default-src` 回退机制
- `object-src 'none'`(禁用插件)
- 没有通配符(`*`)来源
### 4. Cookie (`cookies`)
- 每个 `Set-Cookie` 都设置了 `Secure`、`HttpOnly` 和 `SameSite`
### 5. DNS 与邮件认证 (`dns_email`)
- 发布了 **DMARC** 策略(且未停留在 `p=none`)
- 发布了 **SPF** 记录
- **CAA** 记录限制了证书签发
- 启用了 **DNSSEC**(建议项)
### 6. 重定向 (`redirects`)
- 重定向链长度合理
- 链路中任何地方都没有 HTTPS→HTTP 降级
### 7. 混合内容 (`mixed_content`)
- `https://` 页面上没有引用 `http://` 资源
### 8. HTTP 协议 (`http_protocol`)
- 使用现代 HTTP 协议(HTTP/2 或 HTTP/3)
### 9. 信息披露与 `.well-known` (`wellknown`)
- 根据 RFC 9116 发布了 `security.txt`
## 🧮 评分标准
评级绝不黑盒。完整的评分标准也会通过 **`GET /api/meta`** 实时提供。
**状态得分率**(每种状态能获得该项发现权重的多少):
| status | 得分率 |
|---|---|
| `pass` | 100% |
| `info` | 不参与计分(仅供上下文参考) |
| `warn` | 50% |
| `fail` | 0% |
**类别分数** = 在该类别的各项发现中计算 `sum(权重 × 得分率) / sum(权重)`。
**类别权重**(在最终评级中的相对重要性):
| category | 权重 |
|---|---|
| `tls` | 2.0 |
| `headers` | 2.0 |
| `csp` | 1.5 |
| `dns_email` | 1.5 |
| `cookies` | 1.0 |
| `redirects` | 1.0 |
| `mixed_content` | 1.0 |
| `http_protocol` | 0.5 |
| `wellknown` | 0.5 |
**总得分** = 类别分数的加权平均值 × 100。
**评级区间:** A+ ≥ 95 · A ≥ 90 · B ≥ 80 · C ≥ 70 · D ≥ 60 · F < 60。
*计算示例:* 一个网站除了缺少 HSTS(在 `headers` 类别中算作一次 `fail`,权重 2.0)之外,其他所有项都完美通过,它将失去 `headers` 分数的一部分,然后该部分在平均值中的加权为 2.0。因此,单个高价值项目的缺失会明显拉低评级,完全符合预期设计。
## ⚡ 快速开始
**前置条件:** Python 3.11+(支持 3.13)。就这些:不需要 Node,不需要数据库服务器,不需要云账户。
```
# 1. clone / 进入项目
cd AegisScan
# 2. 创建 virtual environment
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# 3. install
pip install -r requirements.txt
# 4. (可选)复制 env 默认配置;app 也可以在没有 .env 的情况下运行
cp .env.example .env
# 5. run
uvicorn app.main:app --reload
```
现在打开浏览器访问:
- **http://127.0.0.1:8000/**:应用主页(可扫描某个域名)
- **http://127.0.0.1:8000/docs**:交互式 API 文档
- **http://127.0.0.1:8000/api/health**:健康检查
通过命令行扫描:
```
curl -X POST http://127.0.0.1:8000/api/scans \
-H "Content-Type: application/json" \
-d '{"hostname": "github.com"}'
```
### 或者使用 Docker 运行
```
docker build -t aegisscan .
docker run -p 8000:8000 aegisscan
```
### 针对 Postgres 运行(模拟生产环境设置)
```
docker compose up # starts Postgres + the API wired together
```
## ⚙️ 配置
每一个配置项都有安全的默认值;本应用在 **没有 `.env` 文件** 的情况下即可运行。你可以通过环境变量(均以 `AEGIS_` 为前缀) `.env` 文件进行覆盖。详情请参见 `.env.example`。
| 变量 | 默认值 | 用途 |
|---|---|---|
| `AEGIS_ENV` | `development` | `development` / `staging` / `production` |
| `AEGIS_SECRET_KEY` | 开发占位符 | **在任何部署中都必须更改**;用于签发 JWT |
| `AEGIS_DATABASE_URL` | `sqlite:///./aegisscan.db` | SQLite 或 `postgresql+psycopg://…` |
| `AEGIS_CORS_ALLOW_ORIGIN_REGEX` | localhost + `*.vercel.app` | 一个正则表达式即可匹配所有预览主机 |
| `AEGIS_SCAN_TIMEOUT_SECONDS` | `10` | 单次探测的网络超时时间 |
| `AEGIS_ALLOW_PRIVATE_TARGETS` | `false` | **在生产环境中绝对不能设为 `true`**(防范 SSRF) |
| `AEGIS_RATE_LIMIT_PER_MINUTE` | `20` | 针对单个 IP 的扫描提交频率限制 |
| `AEGIS_PUBLIC_BENCHMARKS` | github, stripe, … | 在仪表盘上展示且不进行掩码处理的主机名 |
| `AEGIS_SENTRY_DSN` | 空 | 可选的错误监控 |
生成真实的密钥:
```
python -c "import secrets; print(secrets.token_urlsafe(48))"
```
## 🔌 REST API 参考
完整的交互式文档位于 **`/docs`**(Swagger)和 **`/redoc`**。API 概览:
| 方法 | 路径 | 认证 | 描述 |
|---|---|---|---|
| `GET` | `/api/health` | – | 存活状态 + 版本 |
| `GET` | `/api/meta` | – | 评分标准 + 类别列表 |
| `POST` | `/api/auth/register` | – | 创建账号 |
| `POST` | `/api/auth/login` | – | 获取 JWT(OAuth2 密码表单) |
| `GET` | `/api/auth/me` | ✅ | 获取当前用户 |
| `POST` | `/api/scans` | 可选 | 发起扫描(访客或已认证用户) |
| `GET` | `/api/scans` | ✅ | 你的扫描历史记录 |
| `GET` | `/api/scans/{id}` | – | 获取某次扫描(完整结果) |
| `GET` | `/api/scans/compare?before=&after=` | – | 对比同一主机的两次扫描差异 |
| `GET` | `/api/scans/{id}/report.html` | – | 独立的 HTML 报告 |
| `GET` | `/api/scans/{id}/report.pdf` | ✅ | PDF 报告(认证用户的额外特权) |
| `GET` | `/api/dashboard` | – | 公共汇总统计(主机名已掩码) |
| `GET/POST/DELETE` | `/api/domains` | ✅ | 跟踪你关注的域名 |
访客与已认证用户的区别与 PostureScan 一致:任何人都可以进行扫描;而拥有账户则可解锁历史记录、域名跟踪、扫描对比以及 PDF 导出功能。
## 📄 报告
每次扫描都可以通过三种方式呈现:
- **JSON**:原始的 `POST /api/scans` 响应或 `GET /api/scans/{id}`。机器可读;可直接放入你自己的 CI pipeline 中。
- **HTML**:`GET /api/scans/{id}/report.html`。一个干净、独立的页面。
- **PDF**:`GET /api/scans/{id}/report.pdf`(需认证)。使用 reportlab 生成,因此**无需安装任何系统库**(这是 HTML 转 PDF 工具常见的痛点)。
## 🔒 安全模型与 SSRF 防护
一个会抓取用户提供的 URL 的扫描器天生带有 SSRF 风险。AegisScan 进行了深度防御(`app/guard.py`):
- 主机名会被标准化,且明显的本地名称(`localhost`、`*.local`)会被直接拒绝。
- 主机名会被解析,并且会检查**每一个**解析出的 IP。只要其中任何一个属于私有、环回、链路本地、多播、保留或未指定地址,该扫描就会被拒绝。
- 所有探测都仅限于 **只读** 的 `GET` 流量,并带有固定的、可识别的 User-Agent。
- 基于单 IP 的速率限制可防止滥用。
`AEGIS_ALLOW_PRIVATE_TARGETS=true` 的存在仅仅是为了让你扫描**自己的**实验室环境,绝不能在公开部署中启用。
## 🧪 测试
测试套件 **完全离线运行**:唯一的一处扫描成功路径测试 monkeypatch 了执行引擎,且防护测试 monkeypatch 了 DNS 解析。全程不会连接任何真实的主机。
```
pytest -q
```
测试覆盖范围:
- `test_scoring.py`:评级区间、警告 = 一半分数、info 排除在外、类别加权
- `test_guard.py`:标准化 + 拦截私有/环回 IP 的 SSRF 防护
- `test_masking.py`:仪表板主机名掩码处理 + 基准测试白名单
- `test_checks.py`:针对合成探测数据的单独检查项测试
- `test_api.py`:健康检查、元数据、注册/登录、校验、扫描持久化、认证网关、域名跟踪
CI 会在每次推送到 `main` 或 `staging` 分支时,在 Python **3.11、3.12 和 3.13** 的矩阵环境中运行这些测试(`.github/workflows/ci.yml`)。
## 🗂️ 项目结构
```
AegisScan/
├── app/
│ ├── main.py FastAPI app: CORS, routers, serves the SPA, /docs
│ ├── config.py env-driven settings (safe defaults)
│ ├── database.py SQLAlchemy engine/session (SQLite or Postgres)
│ ├── models.py User, Scan, TrackedDomain
│ ├── schemas.py pydantic request/response models
│ ├── auth.py JWT + bcrypt, guest/auth dependencies
│ ├── guard.py SSRF guard + hostname normalization
│ ├── scoring.py weights, status credit, grade bands
│ ├── masking.py privacy-aware hostname masking
│ ├── reporting.py HTML (Jinja2) + PDF (reportlab)
│ ├── ratelimit.py per-IP sliding window
│ ├── routers/ auth · scans · dashboard · domains
│ └── engine/
│ ├── runner.py orchestrates guard → probe → evaluate → score
│ ├── base.py Finding / ProbeContext types
│ └── checks_*.py one module per category (pure functions)
├── web/ index.html + app.js (the SPA)
├── tests/ pytest suite (offline)
├── .github/workflows/ CI
├── Dockerfile · docker-compose.yml
├── requirements.txt · .env.example · .gitignore · LICENSE
└── README.md
```
## 🚢 部署与 CI/CD
AegisScan 保留了 PostureScan 流水线中行之有效的部分,并去除了曾坑过他们的地方。
**分支模型(已保留):** 在 `staging` 上进行开发,然后通过 **仅限快进** 的方式合并到 `main`。因此,`main` 永远是 `staging` 的严格祖先,没有合并提交,也没有分叉。在合并(提升)之前,CI 必须是绿通状态。
**数据库迁移(已修复):** PostureScan 的数据库迁移会静默失效,因为 Nixpacks 构建器忽略了 Procfile 中的 release 命令行。AegisScan 在启动时会为 SQLite 自动创建表;而对于 Postgres,你需要在一个明确的 pre-deploy 钩子中运行 Alembic(例如 Railway 的 `railway.json` 中的 `preDeployCommand`,或者在你容器的 entrypoint 中执行 `alembic upgrade head`),绝不依赖隐式的 Procfile 命令行。
**CORS(已修复):** 仅使用一条 **正则表达式**(regex)规则(`AEGIS_CORS_ALLOW_ORIGIN_REGEX`)即可匹配每一个临时的预览主机名,因此预览部署永远不会被拒绝。
**驱动/运行时(已修复):** 锁定了 `psycopg` v3(提供 Python-3.13 wheels)和 `sentry-sdk` ≥ 2.61,提前规避了原项目遇到的两次崩溃。
典型的托管设置:在任何支持运行 Dockerfile 的平台(Railway、Fly、Render、Cloud Run)上运行容器(即本代码库),搭配一个托管的 Postgres 数据库(Supabase/Neon/RDS),以及可选的 Sentry。由于前端由同一个进程提供服务,因此不需要同步维护单独的前端部署。
## 🛣️ 路线图
- 定期重新扫描,并在被跟踪域名的评级下降时发送邮件提醒
- 更多类别:证书透明度、IPv6 可达性、更深度的 CSP nonce/hash 校验
- 除了 OWASP 之外,将发现结果映射到合规框架(PCI-DSS、SOC 2)
- 开发一款浏览器插件,在你浏览时显示当前网站的评级
- 多区域扫描,以捕获 CDN/特定区域的连通性问题
- 基于 Redis 的速率限制 + 针对多实例部署的结果缓存
## ⚖️ 负责任地使用
AegisScan 仅对外部可达的 endpoint 执行 **被动的、只读的检查**。请勿将其指向你不拥有或未获得明确测试授权的系统。SSRF 防护默认拦截私有地址空间;在任何共享部署环境中,请务必保持此设置。
## 📜 许可证
MIT。详见 [`LICENSE`](./LICENSE)。
*AegisScan 站在了 PostureScan 的肩膀上。感谢那个团队提出了尖锐的问题,并如实记录了哪些地方出了问题;这里的一些修复方案之所以存在,正是因为他们的幻灯片如此清晰地记录了那些失败。*
标签:AV绕过, Docker, FastAPI, 安全防御评估, 实时处理, 密码管理, 异步扫描, 特征库, 聊天机器人, 请求拦截, 运行时操纵, 逆向工具