damanoreshkan-beep/shodan-lite
GitHub: damanoreshkan-beep/shodan-lite
一个基于 React 和 Node 构建的 Shodan 搜索引擎前端界面,提供智能查询解析和地图可视化功能。
Stars: 0 | Forks: 0
# Shodan-Lite
**一个简单易用的 [Shodan](https://www.shodan.io/) UI —— 输入你想查的,在地图上直观呈现。**
[](LICENSE)
[](https://nodejs.org/)
[](https://react.dev/)
[](https://www.typescriptlang.org/)
[](https://docs.docker.com/compose/)
[](#languages--i18n)

Shodan-Lite 将 Shodan 强大但晦涩的查询语言转化为无需记忆过滤器即可使用的工具,并将结果绘制在带有国家边界和自治系统 (AS) 拓扑的快速 canvas 世界地图上。
## 功能
- **智能查询解析器** —— 输入 IP、域名、普通短语或原始 Shodan 过滤器,它都能正确处理(`8.8.8.8` -> 主机查询,`example.com` -> 主机名搜索,`webcam` -> 限定在选定国家内的文本搜索,`port:554 country:UA` -> 透传)。支持一键预设(摄像头、数据库、RDP 等)。
- **Canvas 世界地图** —— 包含国家边界和聚类主机标记,支持平移/缩放和点击检查。可流畅渲染数千个节点。
- **AS 拓扑** —— 每个主机都与其自治系统以及最多两个上游跃点桥接,让你清楚看到其路由路径。
- **详情面板** —— 以通俗易懂的语言展示服务、Banner、CVE、截图(仅验证 `image/*`)、域名和网络路由。
- **本地化 (i18n)** —— UI 支持 6 种语言,具备自动浏览器语言检测(参见[语言 / i18n](#languages--i18n))。
- **深色 / 浅色主题** —— 具备系统自适应默认值,并在存储被阻止时平滑降级。
- **响应式设计** —— 桌面端三栏布局可折叠为堆叠视图,并在移动端提供底部抽屉详情。
- **内置滥用防护** —— 具备基于 IP 的速率限制和每日 Shodan 额度保护,确保公开的实例不会耗尽你账户的查询额度(参见[安全性](#security))。
## 语言 / i18n
UI 支持 **6 种语言**,由 [i18next](https://www.i18next.com/) + [react-i18next](https://react.i18next.com/) 提供支持,基于访客的浏览器区域设置进行**自动语言检测**([`i18next-browser-languagedetector`](https://github.com/i18next/i18next-browser-languageDetector)),并提供手动切换功能及合理的英语兜底。
| 代码 | 语言 |
| ---- | ----------- |
| `en` | English |
| `uk` | Українська |
| `de` | Deutsch |
| `pl` | Polski |
| `es` | Español |
| `fr` | Français |
翻译字符串位于 `web/src/locales/.json`。要添加语言,只需在同级目录下放入新的 `.json` 并在 i18n 设置中注册即可。
## 快速开始 (Docker)
你唯一需要的是 [Shodan API key](https://account.shodan.io/)(参见[获取 Shodan API key](#get-a-shodan-api-key))。
```
git clone shodan-lite && cd shodan-lite
cp .env.example .env
# 编辑 .env:设置 SHODAN_API_KEY=... 和 PGPASSWORD=...
docker compose up --build
```
打开 。
首次启动时,应用会运行 `db/migrate.mjs`,它会应用 schema,并且如果 AS 表为空,会从 `db/as-data/` 填充 AS 拓扑数据(参见[项目结构](#project-structure))。随后的启动为空操作。
## 配置
在 `.env` 中进行设置(从 `.env.example` 复制):
| 变量名 | 默认值 | 描述 |
| -------------------- | ------------ | --------------------------------------------------------------------------- |
| `SHODAN_API_KEY` | _(必填)_ | 你的 Shodan API key。如果未设置,则回退使用 `~/.config/shodan/api_key`。 |
| `PGPASSWORD` | `changeme` | Postgres 密码(compose 会据此构建 `DATABASE_URL`)。 |
| `DATABASE_URL` | _(compose)_ | Postgres DSN。由 compose 自动设置;仅在非 Docker 开发时需要。 |
| `CACHE_TTL_SEARCH` | `3600` | 缓存搜索结果的秒数。 |
| `CACHE_TTL_HOST` | `86400` | 缓存单个主机详情的秒数。 |
| `RATE_LIMIT_PER_MIN` | `30` | `/api/*` 上每个 IP 的请求上限(超限返回 429)。 |
| `SHODAN_DAILY_LIMIT` | `200` | **实时** Shodan 每日最大调用次数;缓存命中豁免。 |
| `FETCH_TIMEOUT_MS` | `10000` | 上游 Shodan 获取操作的超时时间。 |
## 开发
分别运行 API 和 [Vite](https://vite.dev/) 开发服务器:
```
# 1. Postgres(例如通过 compose db service)
docker compose up -d db
# 2. API server(在 env 中需要 DATABASE_URL + SHODAN_API_KEY)
npm install
npm run migrate # apply schema + seed AS data
npm start # -> http://localhost:8799
# 3. Frontend(将 /api 代理到 :8799)
cd web
npm install
npm run dev # -> http://localhost:5173
```
运行查询解析器测试(不会调用实时 Shodan):
```
npm test
```
此外,还提供零额度 fixture 模式,方便在没有 API key 或数据库的情况下开发 UI:
```
SL_FIXTURE=fixtures/cameras-ua.json PORT=8790 npm start
```
## 架构
```
Browser (React SPA)
│ GET /api/search?q&country&limit GET /api/host/:ip
▼
Node http server (server.mjs)
│ smart query parse (src/query.mjs)
│ rate-limit + daily credit guard
├── Postgres cache (hit -> no live call)
├── Shodan API (live, on miss)
└── AS topology lookup (Postgres as_overview)
```
- **后端** —— 一个单一的原生 `fetch` Node HTTP 服务器(不使用框架)。Postgres 纯粹用作缓存和 AS 拓扑存储(原生 SQL,无 ORM)。搜索结果和主机详情按类型使用各自的 TTL 进行缓存;每小时执行一次 unref 的清理操作以驱逐过期行。
- **前端** —— React 19 + TypeScript (严格模式) SPA,使用 Tailwind CSS v4 和 [shadcn/ui](https://ui.shadcn.com/) (Radix) 组件进行样式设计,配有 canvas-2D 世界地图。由 Vite 构建,并由**同一个 Node 进程**从 `./public`(在 Docker 镜像内由 `web/dist` 创建)提供服务。
### 技术栈
- **后端:** Node 18+ (原生 `fetch`)、Postgres 16、[`pg`](https://node-postgres.com/) (原生 SQL)。
- **前端:** React 19、TypeScript (严格模式)、Vite、Tailwind CSS v4、shadcn/ui (Radix)、i18next、用于地图的 canvas 2D。
- **基础设施:** 多阶段 Dockerfile(构建 SPA,然后由 Node 提供服务)、Docker Compose(app + db)。
## 项目结构
```
shodan-lite/
├── server.mjs # Node HTTP server: /api/search, /api/host/:ip, /healthz, static
├── src/
│ ├── query.mjs # smart query parser (pure, zero-dep) + presets
│ └── db.mjs # lazy pg pool: cache get/set/sweep, AS lookup, ping
├── db/
│ ├── schema.sql # tables (cache, as_overview, …) — all IF NOT EXISTS
│ ├── migrate.mjs # apply schema + seed AS data when empty
│ ├── seed-as.mjs # upsert AS topology from as-data/
│ └── as-data/ # ~500 JSON files: committed, reproducible AS-topology seed
├── tests/query.test.mjs # node-native assertions for the parser (no live calls)
├── fixtures/ # sample responses for SL_FIXTURE UI dev
├── web/ # Vite + React + TS SPA
│ ├── src/
│ │ ├── App.tsx
│ │ ├── locales/ # i18n strings: en, uk, de, pl, es, fr
│ │ ├── lib/{api,classify,categories}.ts
│ │ └── components/{Header,ResultList,MapCanvas,DetailPanel}.tsx + ui/
│ └── public/countries.json # world borders for the map (single source of truth)
├── docs/ # screenshots used in this README
├── Dockerfile # multi-stage: build SPA -> serve via Node
└── compose.yml # app + postgres
```
`db/as-data/` 是特意提交的种子数据(非构建产物),以便 AS 地图能开箱即用。它会在 `as_overview` 为空时由 `migrate.mjs`(或 `npm run seed`)加载一次。
## 截图
| 地图 (深色) | 地图 (浅色) |
| ----------------------------------- | ------------------------------------------ |
|  |  |
| 主机详情 | 移动端 |
| -------------------------------------- | ---------------------------------------- |
|  |  |
## 安全性
该工具会展示互联网暴露的主机;请谨慎对待。
- **额度保护。** `/api/search` 和 `/api/host/:ip` 受到基于 IP 的滑动窗口**速率限制**(`RATE_LIMIT_PER_MIN`,超限返回 429)以及针对**实时** Shodan 调用的全局**每日额度保护**(`SHODAN_DAILY_LIMIT`)。缓存命中和 `/healthz` 豁免。这可以防止未授权的访客通过独特的查询消耗你每月的 Shodan 查询额度。两者均在进程内运行(无需额外服务);对于多实例部署,请在边缘节点放置真正的速率限制器。
- **API key 处理。** 密钥从 `SHODAN_API_KEY`(或 `~/.config/shodan/api_key`)读取,保留在服务器端,且永远不会暴露给浏览器 —— SPA 仅与本地 `/api/*` 代理通信。请将 `.env` 排除在版本控制之外。
- **输入验证。** 查询字符串有长度限制,国家/限制参数会被截断,预设采用白名单机制,主机 IP 在任何上游调用之前都会进行正则检查。
- **不可信输出。** 截图仅接受为 `image/*` 数据 URI;域名链接以 `rel="noopener noreferrer"` + `referrerPolicy="no-referrer"` 方式打开,并标记为外部/不可信 —— 一键导航到攻击者控制的主机名依然存在风险,因此请谨慎点击。
- **无身份验证。** 没有内置的身份验证。如果没有在公开实例前面设置访问控制,请不要使用真实的 API key 暴露该实例。
## 获取 Shodan API key
1. 在 创建或登录 Shodan 账户。
2. 复制你账户页面上显示的 **API Key**。
3. 将其放入 `.env` 作为 `SHODAN_API_KEY=...`(或将其保存到 `~/.config/shodan/api_key`)。
免费账户即可试用 Shodan-Lite;查询额度受限,这正是[额度保护](#security)所要保护的。
## 参考
作者相关的安全研究项目:
- **[ShodanHound](https://github.com/damanoreshkan-beep/ShodanHound)** —— 一种防御工具,可扫描 **公开的** GitHub 仓库以查找 **暴露/泄露的** Shodan API key 并进行验证,以便组织能够在攻击者之前找到并轮换 **自己的** 泄露凭证(负责任的披露 / 漏洞赏金工作流)。
## 许可证
[MIT](LICENSE) © 2026 mrx 标签:Docker, GNU通用公共许可证, MITM代理, Node.js, React, Syscalls, 安全防御评估, 实时处理, 密码管理, 自动化攻击, 请求拦截