MustafaRajihAli/osint-intelligence-platform
GitHub: MustafaRajihAli/osint-intelligence-platform
自托管 OSINT 调查平台,集成八个数据收集模块、关系图关联、风险评分和离线 AI 分析,支持案件管理与报告导出。
Stars: 0 | Forks: 0
# 🛰️ OSINT 情报平台
**一个自托管的 OSINT 工作台 —— 包含八个收集模块、一个关系图,以及一个可完全离线运行的可插拔 AI 分析师。**
[](https://www.python.org/)
[](https://fastapi.tiangolo.com/)
[](https://nextjs.org/)
[](https://www.postgresql.org/)
[](https://redis.io/)
[](./docker-compose.yml)
[](./backend/app/crypto.py)
[](./backend/app/ai/base.py)
[模块](#collection-modules) · [快速开始](#quick-start) · [架构](#how-it-fits-together) · [安全](#security-posture) · [状态](#status)
## 这是什么
一个用于**授权研究和教育**的基于浏览器的调查平台。你注册一个本地账号,对目标运行收集模块,将发现的结果保存到案例中,平台会将该案例关联成一个关系图、一张地图、一个风险表,以及一份由 AI 编写的情报简报 —— 然后将所有内容导出为 JSON 或 PDF。
有两个属性决定了它的所有其他行为:
## 架构组合方式
```
flowchart TD
UI["🖥️ frontend/ Next.js 14investigate · cases · graph · map · wayback · audit · admin"] UI -->|"JWT REST"| API["api/ FastAPI routers
auth · osint · ai · investigations · audit · admin"] UI <-->|"WebSocket"| API API --> RBAC{{"🔐 auth.py
bcrypt + JWT · require_role()"}} RBAC -->|"role too low"| DENY(["403 — no collection performed"]) RBAC -->|"admin / analyst"| Q Q["arq job queue on Redis"] --> W["tasks.py worker
runs the module off the request thread"] W --> REG["osint/ registry
8 self-registering modules"] REG --> SRC["🌐 public sources
DNS · WHOIS · TLS · crt.sh · platform profiles
GitHub API · ip-api · Wayback · Wikipedia · DDG"] REG -.->|"optional keys"| KEYED["🔑 Shodan · VirusTotal · HIBP · Hunter"] W -->|"progress events"| PS["Redis pub/sub
scan:{job_id}"] PS -->|"live scan feed"| UI W --> RESULT(["finding JSON"]) RESULT --> ENC["🔒 crypto.py EncryptedJSON
Fernet before it touches disk"] ENC --> DB[("🗄️ Postgres
users · investigations · findings · audit_log")] DB --> CORR["graph.py · geo.py · risk.py
correlate · locate · score"] CORR --> OUT["📤 reports.py PDF · JSON export
Cytoscape graph · Leaflet map"] DB --> AI["ai/ provider abstraction
summarize · extract entities · explain diff"] AI --> LLM["🧠 Anthropic · OpenAI · Gemini · Ollama (local)"] API --> AUDIT["📝 audit.py
who ran what, against which target, from which IP"] AUDIT --> DB style RBAC fill:#b45309,stroke:#f59e0b,stroke-width:3px,color:#fff style ENC fill:#14532d,stroke:#22c55e,color:#fff style DENY fill:#7f1d1d,stroke:#ef4444,color:#fff style LLM fill:#1e3a5f,stroke:#3b82f6,color:#fff ``` 扫描绝不是一个阻塞请求。`POST /api/osint/scan/async` 会立即返回一个 `job_id`,worker 将进度发布到 `scan:{job_id}` 频道,浏览器通过 WebSocket 监听该频道 —— 因此,针对 15 个平台的用户名扫描或 `crt.sh` 子域名拉取将以流式传输返回,而不是让请求挂起 30 秒。 ## 收集模块 每个模块都是 `OsintModule` 的子类,并实现一个方法:`async run(target) -> dict`。子类化**即是**注册过程 —— `__init_subclass__` 会将实例放入注册表,并在下次启动时出现在 UI 的下拉菜单中。 | 模块 | 目标 | 收集内容 | 设计说明 | |---|---|---|---| | 👤 **username** | 用户名 | 15 个平台上的存在情况 —— GitHub, Reddit, X, Instagram, TikTok, YouTube, Pinterest, GitLab, HN, Medium, Dev.to, StackOverflow, Keybase, ProductHunt, Twitch | 所有 15 项检查均通过 `asyncio.gather` **并发**运行,因此实际耗时仅为一次请求而非十五次。判定存在需要返回 `200` **且** body 大于 500 字节 —— soft-404 页面通常会返回 200 状态码但附带一个几近空白的页面外壳 | | 📧 **email** | 邮箱地址 | MX 记录、可达性提示、Gravatar URL、HIBP 数据泄露检查 | Gravatar URL 是在本地通过地址的 MD5 加上 `?d=404` 生成的,因此只要命中,就能在没有中间查询服务的情况下证明存在已注册的头像 | | 🌐 **domain** | 域名 | DNS (A/AAAA/MX/NS/TXT/CNAME)、WHOIS、TLS 证书 + SAN、HTTP 头、**来自证书透明度的子域名**、Shodan,以及 Wikipedia/DDG/HackerTarget 丰富化数据 | 七个收集器在一个 `asyncio.gather` 中运行,阻塞式库(`python-whois`、TLS 握手)会被推送到线程执行器中,这样它们就不会阻塞事件循环。子域名来自 **crt.sh** —— CT 日志是权威的且无需密钥,这不同于基于字典的暴力破解 | | 📞 **phone** | E.164 号码 | 有效性、地区、运营商、线路类型、时区、规范格式 | 纯粹使用 `phonenumbers`(Google 的 libphonenumber) —— 离线元数据,无需查询 API,因此受调查的号码绝不会泄露给第三方 | | 🖥️ **ip** | IPv4/IPv6 | 反向 DNS、ASN/ISP/地理位置、Shodan 主机记录、VirusTotal 信誉 | 在报告之前会对地址进行分类(`is_private` / `is_global` / `is_reserved`),因此 RFC1918 结果会被明确标记,而不会作为互联网主机呈现 | | 🔗 **url** | URL | 状态链、最终 URL、标题、Open Graph + Twitter cards、链接、cookies、请求头、**技术指纹**、Wayback 可用性 | 请参阅下文的指纹识别部分 —— 58 个加权特征签名,而不是仅仅读取一下 `Server:` 头 | | 🐙 **github** | 用户名 | 个人资料、组织、仓库、星标/分支、语言 + 主题分布、按 UTC 小时和工作日划分的活动直方图、**在提交元数据中泄露的电子邮件** | 提交邮箱探测是最有趣的部分:`git config user.email` 经常会暴露个人资料中设置为隐私的个人邮箱。它会遍历前 5 个非 fork 仓库,跳过 `noreply.github.com`,并丢弃已经公开的个人资料邮箱,因此保留下来的才是真正的新发现 | | 🖼️ **image** | 图片 URL | 尺寸、格式、SHA-256 + MD5、完整的 EXIF、**以十进制坐标表示的 GPS**、5 个引擎的反向搜索链接 | 反向搜索以**链接而非抓取**的形式提供 —— Google/Yandex/TinEye 的条款禁止自动查询,因此由分析师手动点击进入。服务器端的工作重心放在 EXIF 上,因为 GPS、相机序列号和原始时间戳实际上都存储在这里 |
添加模块 —— 完整的约束条件
``` # backend/app/osint/asn.py from .base import OsintModule class AsnModule(OsintModule): name = "asn" target_type = "asn" async def run(self, target: str) -> dict: return {"target": target, "prefixes": [...]} ``` 在 `backend/app/osint/__init__.py` 中导入它,你就完成了 —— `__init_subclass__` 会注册它,`GET /api/osint/modules` 会报告它,Investigate 下拉菜单会列出它,并且 `POST /scan/async` 可以调度它。 有两个可选的 hook 可以使其成为一等公民: - 来自 `progress.py` 的 `emit("source", site=...)` 会将实时进度流式传输到浏览器。在 job 上下文之外,它是一个 **no-op**,因此同一个模块依然可以在测试或 REPL 中同步运行。 - `risk.py` 中的 scorer 和 `graph.py` 中的 extractor 将该模块纳入风险表和关系图中。这两者都是以模块名称为键的普通字典查找 —— 没有对应条目的模块会被跳过,而绝不会引发崩溃。
技术指纹识别 —— 为什么用 58 个签名而不是读取头部
`Server: nginx` 几乎说明不了任何问题,而且 `X-Powered-By` 经常会被抹除。`fingerprint.py` 会将每个签名与 **五个独立的信号** 进行匹配,并根据每个信号的可伪造程度对其权重进行累积和置信度计算: | 信号 | 权重 | 原因 | |---|---|---| | HTTP header | 40 | 由技术栈本身发出,极少被伪造 | | Cookie name | 35 | `PHPSESSID`, `csrftoken` —— 由框架设置 | | Meta generator | 30 | 由 CMS 写入,有时会被移除 | | Script `src` | 25 | 对于托管的 SDK 信号很强,对于自托管的副本则较弱 | | HTML body | 15 | 最容易被偶然匹配到,因此其价值最低 | ``` tech = fingerprint(html, headers, scripts, meta_tags, cookies) # {"cms": [{"name": "WordPress", "signals": ["html", "meta"], "confidence": 45}], # "language": [{"name": "PHP", "signals": ["implied:WordPress"], "confidence": 50}], ...} ``` 签名声明了 `implies`(例如,WordPress ⇒ PHP,Next.js ⇒ React + Node.js),因此一次匹配就会推导出它所必然依赖的技术栈。未匹配到的 `Server` / `X-Powered-By` 值依然会被原样展示 —— 一个未知的技术栈应该看起来是未知的,而不是被默默丢弃。覆盖范围涵盖 CMS、电子商务、JS 框架、分析工具、标签管理器、支付、CDN、托管、服务器、运行时和安全标头,并且每个模式在导入时仅编译一次。
不使用 Docker 进行本地开发
``` docker compose up db redis # or point DATABASE_URL / REDIS_URL at your own ``` ``` cd backend python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate pip install -r requirements.txt alembic upgrade head uvicorn app.main:app --reload arq app.tasks.WorkerSettings # separate shell — async scans need the worker ``` ``` cd frontend npm install npm run dev ``` `app.main` 仅为 `http://localhost:3000` 挂载了 CORS。如果要从其他源为前端提供服务,则意味着需要修改 `backend/app/main.py` 中的 `allow_origins`。
使用 Ollama 完全离线运行 AI 层
``` ollama pull llama3.2 ``` Ollama 监听 `localhost:11434`;backend 通过 `OLLAMA_BASE_URL` 连接它,该变量默认为 `http://host.docker.internal:11434` —— 这适用于 Windows/macOS 上的 Docker Desktop。在 Linux 上,请使用宿主机 IP 或者在同级容器中运行 Ollama。在 **Settings** 中选择 `OLLAMA`;模型下拉列表会通过 `/api/tags` 实时填充,因此你可以从实际已拉取的模型中进行选择。 请求一个尚未拉取的模型会返回已安装列表以及确切的命令,而不是一个简单的 404: ``` Ollama model 'llama3.1' is not pulled. Installed models on host: llama3.2:latest. Either set the model in Settings to one of these, or run `ollama pull llama3.1` on the host. ``` 在 Settings 中切换提供商**会重置模型名称** —— 否则 `gpt-4o-mini` 会被发送给 Ollama 并以 404 失败,看起来就像是安装损坏了一样。
专为经过授权的 OSINT 研究和教育而构建。请按此初衷合理使用。
标签:AI风险缓解, AV绕过, ESC4, FastAPI, LLM评估, Ollama, OSINT, 动态调用, 实时处理, 情报收集, 搜索引擎查询, 本地部署, 测试用例, 漏洞研究, 请求拦截, 逆向工具