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: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![Node](https://img.shields.io/badge/Node-%E2%89%A518-339933?logo=node.js&logoColor=white)](https://nodejs.org/) [![React](https://img.shields.io/badge/React-19-61DAFB?logo=react&logoColor=black)](https://react.dev/) [![TypeScript](https://img.shields.io/badge/TypeScript-strict-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/) [![Docker](https://img.shields.io/badge/Docker-Compose-2496ED?logo=docker&logoColor=white)](https://docs.docker.com/compose/) [![i18n](https://img.shields.io/badge/i18n-6%20languages-orange.svg)](#languages--i18n) ![Shodan-Lite 地图 UI](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/7c2967a23e141348.png)
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`)加载一次。 ## 截图 | 地图 (深色) | 地图 (浅色) | | ----------------------------------- | ------------------------------------------ | | ![深色主题](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/7c2967a23e141348.png) | ![浅色主题](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/6ef2c134d8141353.png) | | 主机详情 | 移动端 | | -------------------------------------- | ---------------------------------------- | | ![详情面板](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/7452ecb502141358.png) | ![移动端布局](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/735d714439141403.png) | ## 安全性 该工具会展示互联网暴露的主机;请谨慎对待。 - **额度保护。** `/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, 安全防御评估, 实时处理, 密码管理, 自动化攻击, 请求拦截