DreadpiratePickles/patch-peeker

GitHub: DreadpiratePickles/patch-peeker

一款授权 Web 应用安全扫描器,以礼貌爬虫和良性探测标记常见输入输出处理弱点,生成按优先级排序的修复报告。

Stars: 0 | Forks: 0

# 🩹 Patch Peeker ### 爬取已授权的 Web 应用并标记常见的输入和输出处理弱点 —— 然后为你提供一份带有证据支持且按优先级排序的修复报告。 ![python](https://img.shields.io/badge/python-3.11%2B-3776AB?logo=python&logoColor=white) ![license](https://img.shields.io/badge/license-MIT-blueviolet) ![tests](https://img.shields.io/badge/tests-38%20passing-brightgreen) ![interface](https://img.shields.io/badge/interface-CLI%20%2B%20Web-22c55e) ![crawler](https://img.shields.io/badge/crawler-BeautifulSoup-3776AB) ![transmits](https://img.shields.io/badge/transmits-nothing%20by%20default-success) *一个对什么都大呼小叫的扫描器,就是没人看的扫描器。*
Patch Peeker 是一个小巧、诚实且*防御性*的初步 Web 评估工具。它会礼貌地进行访问(可识别的 User-Agent、仅限同源、有限速),记录所有内容,并且绝不会进行任何破坏性操作。它专为**你拥有或获得明确授权**的系统而设计。 它不能替代深入的手动测试或完整的 DAST 套件。它是你首先运行的工具,用于将“有人应该看看这个应用”的念头,转化为一份按优先级排序且具体的、可修复的漏洞清单。 ## 检查内容 | 类别 | 检查项 | 方式 | | --- | --- | --- | | `headers` | 缺失 Content-Security-Policy | 被动(响应头) | | `headers` | 缺失点击劫持保护(X-Frame-Options / CSP `frame-ancestors`) | 被动 | | `headers` | HTTPS 下缺失 HSTS | 被动 | | `headers` | 缺失 `X-Content-Type-Options: nosniff` | 被动 | | `headers` | 缺失 Referrer-Policy | 被动 | | `headers` | 版本信息泄露(`Server`, `X-Powered-By`, …) | 被动 | | `cookies` | `Set-Cookie` 缺失 HttpOnly / Secure / SameSite | 被动 | | `csrf` | 没有类似 CSRF token 字段的 POST 表单 | 被动 | | `forms` | 没有命名输入的表单(无法测试) | 被动 | | `injection` | 单引号探测后的 SQL 错误泄露 | **主动**(良性) | | `injection` | 反射型 HTML 注入(未编码的标记) | **主动**(良性) | 被动检查仅读取服务器已发送的内容。主动检查会将*良性*值(一个单引号 `'` 和一个无害的标记标签)提交到表单字段中,并查看响应。使用 `--passive` 时会完全跳过主动检查。 ## 工作原理(数据流) ``` --i-have-authorization (required for non-local targets) │ validate_url ──▶ require_authorization ──▶ crawl ──▶ [CrawledPage, …] ──▶ checks ──▶ dedupe+rank ──▶ Findings (scheme/host (consent gate) (same-origin, │ ├─ assess_headers │ allowlist) bounded, polite) │ ├─ assess_cookies ├─▶ rich table + summary │ ├─ assess_forms ├─▶ --json (pure) │ └─ probe_form (unless └─▶ --out report.md │ --passive) ▼ html · headers · Set-Cookie ``` 1. **`validate_url`** —— 拒绝任何非 `http`/`https` 的内容(拒绝 `file://`, `ftp://`, `gopher://`, …)以及任何无主机的 URL,*在打开套接字之前*。 2. **`require_authorization`** —— 允许 localhost/私有 IP 目标;其他任何目标都需要 `--i-have-authorization`。位置判断仅基于字面主机名,绝不依赖 DNS 解析(保守的选择)。 3. **`crawl`** —— 广度优先,**仅限同源**,受 `--max-pages` 限制。每个请求都通过 `--delay` 进行限速,在 `--timeout` 后超时,最多跟随 5 次重定向,如果落在源之外则被丢弃。响应体最多读取 3 MB 上限,因此恶意目标无法耗尽内存。死链/缓慢/阻塞的页面会被跳过,绝不致命。 4. **checks** —— 每个爬取的页面都会输入到被动的标头/cookie/表单检查中;除非使用 `--passive`,否则表单还将额外进行主动探测。 5. **dedupe + rank** —— 跨页面的相同发现合并为一个;结果按严重程度排序:critical → high → medium → low → info。 6. **render** —— 为人类提供颜色编码的表格 + 摘要面板,为机器提供纯 JSON,和/或 Markdown 报告文件。 **数据模型**(无数据库):`CrawledPage`(url, final_url, status, headers, html, 原始 Set-Cookie 行)和 `Finding`(severity, title, url, evidence, remediation, confidence, category)。 ## 安装 ``` cd patch-peeker python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate pip install -r requirements.txt pip install -e . # provides the `patch-peeker` command cp .env.example .env # optional defaults ``` 没有可编辑安装?一切也可以通过 `PYTHONPATH=src python -m patch_peeker.cli …` 运行。 ## CLI 用法 全局标志(在子命令之前或之后使用):`--plain` / `--no-color`(也遵循 `NO_COLOR` 环境变量和非 TTY 输出),`--version`。 ### `scan` ``` patch-peeker scan --url URL [--i-have-authorization] [--max-pages N] [--delay S] [--timeout S] [--passive] [--user-agent UA] [--out FILE] [--json] ``` | 标志 | 默认值 | 含义 | | --- | --- | --- | | `--url` | *(必填)* | 目标 URL(仅限 `http`/`https`)。 | | `--i-have-authorization` | off | 对于**非本地**目标是必填的。你声明你拥有它或拥有书面许可。 | | `--max-pages` | 12 | 爬取的最大页面数(环境变量 `PATCH_PEEKER_MAX_PAGES`)。 | | `--delay` | 0.2 | 每次请求之间的礼貌延迟(秒)。 | | `--timeout` | 10 | 每次请求的超时时间(秒)。 | | `--passive` | off | 跳过主动表单探测;仅进行被动检查。 | | `--user-agent` | `patch-peeker/2.0 (authorized web assessment)` | 覆盖发送的 UA。 | | `--out FILE` | — | 同时写入一份 Markdown 报告。 | | `--json` | off | 向 stdout 输出**纯** JSON(绝无任何装饰)。 | **退出代码:** `0` 干净 / 仅 low 级别,`2` 至少有一个 high/critical 发现(在 CI 中很有用),`1` 发生错误(错误输入,拒绝授权)。 **示例** ``` # Localhost — 不需要 authorization flag: patch-peeker scan --url http://127.0.0.1:5000 # 你拥有 / 被授权测试的远程目标: patch-peeker scan --url https://staging.example.com --i-have-authorization --max-pages 20 # 温和的 passive-only pass(不提交 payloads): patch-peeker scan --url http://127.0.0.1:8080 --passive # 适用于 pipeline 的 machine-readable 格式: patch-peeker scan --url http://127.0.0.1:5000 --json > findings.json # 咨询级的 Markdown 交付物: patch-peeker scan --url http://127.0.0.1:5000 --out report.md ``` **输出示例** ``` ┌──────────────── 🩹 patch-peeker ─────────────────┐ │ crawl authorized web apps, flag input-handling weaknesses │ └───────────────────────────────────────────────────┘ Findings for http://127.0.0.1:8791 ┌────┬──────────┬──────────┬─────────────────────────────────────────┬───────────────────────┐ │ # │ Severity │ Category │ Title │ URL │ ├────┼──────────┼──────────┼─────────────────────────────────────────┼───────────────────────┤ │ 1 │ MEDIUM │ headers │ Missing Content-Security-Policy header │ http://127.0.0.1:8791 │ │ 2 │ MEDIUM │ headers │ Missing clickjacking protection │ http://127.0.0.1:8791 │ │ 3 │ MEDIUM │ csrf │ POST form missing obvious CSRF token │ http://127.0.0.1:8791/│ │ 4 │ LOW │ headers │ Missing X-Content-Type-Options: nosniff │ http://127.0.0.1:8791 │ │ 5 │ INFO │ headers │ Missing Referrer-Policy header │ http://127.0.0.1:8791 │ └────┴──────────┴──────────┴─────────────────────────────────────────┴───────────────────────┘ ┌───────────────────────── summary ──────────────────────────┐ │ 3 medium · 1 low · 1 info │ │ 5 findings — 0 high/critical needing attention │ └─────────────────────────────────────────────────────────────┘ ``` ### `serve` 本地 Flask UI:粘贴一个 URL,勾选授权框,在浏览器中阅读报告。 ``` patch-peeker serve [--host HOST] [--port PORT] [--debug] ``` | 标志 | 默认值 | 备注 | | --- | --- | --- | | `--host` | `127.0.0.1` | 绑定到 loopback。**非 loopback 绑定会受到警告** —— 此 UI 会抓取任意 URL(属于 SSRF 攻击面);不要将其暴露在外。 | | `--port` | `5070` | 环境变量 `PATCH_PEEKER_PORT`。 | | `--debug` | off | **不安全** —— 启用 Werkzeug 的交互式调试器(可导致任意代码执行)。会发出警告;切勿在暴露的端口上使用。 | ``` patch-peeker serve # http://127.0.0.1:5070 ``` ## 安全与授权 - **仅限已授权目标。** 远程扫描需要 `--i-have-authorization`;UI 需要勾选所有权复选框。这是你声明你拥有目标或持有书面许可的凭证。未经授权的扫描可能是违法的。 - **天生防御性。** 仅进行同源爬取;主动探测仅提交*良性*值(`'` 和一个惰性标记)—— 没有破坏性输入,没有利用,没有数据泄露。 - **有界且礼貌。** `--max-pages`,`--delay`,`--timeout`,5 次重定向限制,3 MB 响应限制,以及诚实可识别的 User-Agent。 - **感知恶意输入。** 不受信任的响应字节有大小限制,被宽松解码,并以纯文本形式呈现(绝不重新解释为终端标记)。输出流被强制为 UTF-8,因此奇怪的字节不会导致 Windows 控制台崩溃。 - **无秘密泄露。** 绝不打印 Cookie 的*值* —— 仅打印 Cookie 名称和标志状态。 - **安全的默认设置。** `serve` 绑定到 `127.0.0.1`;调试模式是可选的,并且会发出强烈警告。 已知局限性(设计使然):CSRF/标头检测是启发式的,无法看到未向其展示的服务器端标头/SameSite 控制;认证区域需要你扩展会话处理;仅被动模式无法找到反射/SQL 错误问题。 ## 开发与测试 ``` cd patch-peeker PYTHONPATH=src python -m pytest -q PYTHONPATH=src python -m patch_peeker.cli --help ``` 测试快速且封闭 —— 没有真实网络,没有 sleep。网络相关代码(爬虫、探测、Cookie 提取)通过内存中的 fake 进行测试,因此整套测试可以在不到一秒的时间内运行完毕。 **前提条件:** Python 3.11+,以及 `flask`,`requests`,`beautifulsoup4`,`rich`(参见 `requirements.txt`)。 ## 🏷️ 为什么叫 "Patch Peeker"? Peek(偷看),而不是 Poke(捅破)。它会礼貌地爬取,安全地探测,并且在你声明已获授权之前拒绝运行。返回的结果根据你应该首先修复的内容进行排序,并在每个发现后附上证据 —— 因为真正的漏洞和扫描器的白日梦之间的区别,就在于它是否能向你展示其推导过程。 ## 🔬 这个工具是如何构建的 **目的。** 这是你首先运行的工具:将其指向你拥有的 Web 应用,它会礼貌地爬取、检查和探测它,然后返回一份按优先级排序、有据可查的修复报告 —— 在终端中看起来很漂亮,在 pipeline 中是纯 JSON,天生安全。 **工作原理。** 它爬取同源链接(每次抓取各自容错),运行被动检查(安全标头、Cookie 标志、版本披露),并且 —— 在 `--passive` 模式之外 —— 运行温和的主动探测,将跨页面的发现折叠到各个类别中,并按严重性对它们进行排名。`--out` 会编写一份咨询级别的 Markdown 报告,同时保持 stdout 整洁。 **出色的 CLI。** 一个显示当前目标和流逝时间的实时进度旋转器,一个颜色编码的发现表格(红色代表 high/critical,黄色代表 medium,青色代表 low,灰色代表 info),带有按严重性划分的摘要面板和结论行,`--passive` 模式,`--timeout`/`--user-agent`/`--delay` 标志,以及保留的退出代码契约(0 干净,2 high/critical,1 错误)。 **关键改进。** *功能性:* 修复了一个爬虫崩溃问题,即一个无法访问或缓慢的链接会导致整个爬取过程中止;修复了 Flask `/scan` 在格式错误的 `max_pages` 上报 500 错误的问题(现在限制在 1..25);修复了 Windows 上在横幅/爬取文本上的 `UnicodeEncodeError`;添加了被动标头和 Cookie 标志检查以及 `--passive` 模式。*安全性:* 一个协议/主机白名单在任何套接字打开之前拒绝 `file://`, `ftp://`, `gopher://`, `javascript:` 和无主机的 URL;许可关卡(`--i-have-authorization`,位置由字面主机判断,而非 DNS);一个同源重定向范围守卫阻止开放重定向驱动的 SSRF/范围蔓延;3 MB 的流式单次响应正文限制;不受信任的爬取文本作为惰性 `rich.Text` 渲染;绝不打印 Cookie *值*,仅打印名称和标志状态。 **示例。** ``` PYTHONPATH=src python -m patch_peeker.cli scan --url http://127.0.0.1:8791 --delay 0 ``` ## ⚖️ 授权使用与安全章程 **这些是用于你拥有或获得明确授权进行评估的系统、文件、网络和人员的防御性工具。** 在运行任何内容之前阅读本文: - **授权不是可选的。** 网络钓鱼模拟、网络扫描、IP 信誉查询和 Web 应用探测都会触及他人的系统或数据。请先获得书面授权。有几个工具在你声明之前*拒绝行动*(`--yes`,`--authorized-training`,`--i-have-authorization`,`--i-am-authorized` 等)。 - **默认安全。** 每个 Web UI 都绑定到 `127.0.0.1` (loopback)。出口流量、实时发送和主动扫描都受显式标志控制 —— 演练、被动或拒绝并警告始终是默认设置。 - **没有意外的自毁行为。** Flask `--debug`(Werkzeug 交互式调试器在任何回溯上都是远程代码执行)会受到强烈警告,并且在离开 loopback 时被完全拒绝。不受信任的输入有大小限制,经过验证,并被惰性渲染,因此恶意文件或日志行不会导致你的终端 —— 或接管。 - **输出中没有秘密。** API key 保留在请求标头中,密码来自环境,并且不会记录或打印任何敏感信息。 这些不是攻击性工具。它们不包含漏洞利用、不包含凭证收集器、也不包含 payload。如果某个工具*可能*被滥用,那么它天生就被设计为能够抵御这种滥用。 ## 🧪 开发 ``` python -m venv .venv && source .venv/bin/activate pip install -e . pytest -q # 38 tests, no network, no sleeps patch-peeker --help ``` ## 📄 许可证 在 **MIT License** 下发布。参见 [LICENSE](LICENSE)。
防御性工具。无漏洞利用,无 payload,无凭证收集器。
标签:BeautifulSoup, Python, Splunk, Web应用扫描, 主机安全, 安全合规检测, 文档结构分析, 无后门, 逆向工具