Felps03/NoSQL-Injection
GitHub: Felps03/NoSQL-Injection
一个教育性质的 NoSQL 注入 POC 项目,通过脆弱与安全两条登录路由的对比演示攻击原理及 Zod 校验防护方案。
Stars: 1 | Forks: 0
# NoSQL Injection POC
[](https://github.com/Felps03/NoSQL-Injection/actions/workflows/ci.yml)
[](#运行测试)
[](./LICENSE)
[](https://nodejs.org)
两个完全相同的登录路由。向它们发送相同的恶意 payload:一个让你无需密码即可登录,另一个则通过 `400 Bad Request` 将你拦截。这就是曾经导致各地生产系统崩溃的同一种漏洞——现在它在本地运行,完全由你掌控,准备好供你研究、破解并真正理解它。
## ✨ 亮点
- 🎯 **真实攻击,现场演示** — 同样的路由,同样的 payload:一个无需密码即可认证,另一个则进行拦截。
- ⚡ **现代技术栈** — Node 24, Express 5, Mongoose 8, 100% ESM,零 Babel。
- 🛡️ **真正的缓解措施** — 使用 [Zod](https://zod.dev/) 进行 schema 验证,而不是博客里那种通用的“清理”。
- 🧪 **真正的测试** — 使用 `mongodb-memory-server` 进行集成测试,覆盖率超过 90%。
- 🐳 **一键启动** — `docker compose up --build`,API 即刻上线并连接到 MongoDB。
- 📄 **深度文档** — 包含 OpenAPI、Postman collection 以及逐步展示攻击流程的 Mermaid 图表。
## 目录
- [亮点](#-destaques)
- [关于项目](#sobre-o-projeto)
- [安全警告](#-aviso-de-segurança)
- [技术栈](#-stack)
- [环境要求](#requisitos)
- [本地安装](#instalação-local)
- [环境变量](#variáveis-de-ambiente)
- [本地运行](#-rodando-localmente)
- [使用 Docker 运行](#-rodando-com-docker)
- [运行测试](#-rodando-os-testes)
- [可用脚本](#scripts-disponíveis)
- [API 路由](#rotas-da-api)
- [示例:创建用户](#exemplo-criar-usuário)
- [示例:易受攻击的登录](#-exemplo-login-vulnerável)
- [示例:安全的登录](#-exemplo-login-seguro)
- [图表:易受攻击 vs. 安全](#-diagrama-vulnerável-vs-seguro)
- [如何防止 NoSQL Injection](#-como-prevenir-nosql-injection)
- [现代化决策](#-decisões-de-modernização)
- [文件夹结构](#-estrutura-de-pastas)
- [CI](#-ci)
- [后续计划](#-próximos-passos)
## 关于项目
本项目是一个教育性质的概念验证 (POC),以实践方式演示了在使用 MongoDB 的 Node.js API 中 NoSQL Injection 是如何发生的——以及正确的输入验证(使用 [Zod](https://zod.dev/))如何化解同类攻击。
该 API 暴露了两个等效的登录路由:一个故意设计为易受攻击,另一个则是安全的,以便进行并排对比。这不是一篇关于安全的纯理论文章——而是运行中、被利用、被修复的漏洞,包含你可以几分钟内克隆并测试的真实代码。
## ⚠️ 安全警告
本项目**仅用于学习和防御意识提升目的**,应在本地/受控环境(你的机器、隔离的 Docker container)中运行。它不应被公开暴露、用于攻击第三方系统,也不应被视为攻击工具。目的是理解漏洞从而知道如何预防它,而不是在这个学习环境之外利用它。
## 🧰 技术栈
- Node.js 24
- Express 5
- MongoDB
- Mongoose 8
- Zod
- Jest
- Supertest
- mongodb-memory-server
- Docker
- Docker Compose
## 环境要求
- Node.js 24+
- npm
- Docker 和 Docker Compose(如果打算通过 container 运行)
## 本地安装
```
npm install
```
## 环境变量
`.env.example` 文件记录了应用使用的变量:
```
NODE_ENV=development
PORT=3333
MONGO_URL=mongodb://localhost:27017/nosql-injection
```
- `NODE_ENV`:控制运行模式(在 `development` 模式下,未处理的错误会在响应正文中返回 stack trace)。
- `PORT`:服务器监听的 HTTP 端口。
- `MONGO_URL`:MongoDB 的连接字符串。这是必填项——如果未设置,应用启动时会失败。
将该文件复制为 `.env` 并根据需要进行调整:
```
cp .env.example .env
```
## 🚀 本地运行
确保在 `localhost:27017` 有可用的 MongoDB:
```
MONGO_URL=mongodb://localhost:27017/nosql-injection npm run dev
```
或者,使用已配置好的 `.env` 文件:
```
npm run dev
```
## 🐳 使用 Docker 运行
```
docker compose up --build
```
这会同时启动 API 和 MongoDB,并且它们已经互联。要验证 API 是否已上线:
```
curl http://localhost:3333/health
```
停止运行:
```
docker compose down
```
## 🧪 运行测试
```
npm test
```
集成测试使用了 [mongodb-memory-server](https://github.com/typegoose/mongodb-memory-server),它会自动启动一个内存中的 MongoDB。无需运行真实的 MongoDB 即可进行测试。
`npm test` 还会在终端(以及 `__tests__/coverage` 中)生成覆盖率报告。README 顶部的覆盖率徽章是最后一次数据的截取——如果覆盖率发生显著变化,请记得更新它。
## 可用脚本
- `npm start` — 以生产模式启动应用。
- `npm run dev` — 启动应用并实现自动重载 (`node --watch`)。
- `npm test` — 运行测试套件 (Jest + Supertest)。
- `npm run lint` — 使用 ESLint 检查代码。
- `npm run lint:fix` — 自动修复可以修复的问题。
- `npm run format` — 使用 Prettier 格式化代码。
- `npm run format:check` — 检查格式而不修改文件。
## API 路由
| 方法 | 路由 | 描述 |
| ------ | ------------------------ | ------------------------------------------- |
| GET | `/health` | 检查 API 是否在线 |
| GET | `/users` | 列出已注册的用户 |
| POST | `/users` | 创建用户 |
| POST | `/auth/vulnerable/login` | 易受 NoSQL Injection 攻击的登录 |
| POST | `/auth/safe/login` | 受 Zod 验证保护的登录 |
## 示例:创建用户
```
curl -X POST http://localhost:3333/users \
-H "Content-Type: application/json" \
-d '{"email":"teste@example.com","password":"123456"}'
```
## 🔓 示例:易受攻击的登录
使用正确密码的常规登录:
```
curl -X POST http://localhost:3333/auth/vulnerable/login \
-H "Content-Type: application/json" \
-d '{"email":"teste@example.com","password":"123456"}'
```
NoSQL Injection 的教育性 payload —— 它不发送密码,而是发送一个 MongoDB 运算符 (`$gt`,即“大于空字符串”),该条件始终为真:
```
curl -X POST http://localhost:3333/auth/vulnerable/login \
-H "Content-Type: application/json" \
-d '{"email":"teste@example.com","password":{"$gt":""}}'
```
该路由在不知道真实密码的情况下对用户进行了认证。发生这种情况是因为 `req.body` 被直接传递给了 Mongoose 的查询 (`User.findOne({ email, password })`),而没有验证 `password` 字段的类型。**故意**将该路由设计为易受攻击的,以作演示之用。
## 🔒 示例:安全的登录
使用正确密码的常规登录:
```
curl -X POST http://localhost:3333/auth/safe/login \
-H "Content-Type: application/json" \
-d '{"email":"teste@example.com","password":"123456"}'
```
相同的注入 payload,现在已被拦截:
```
curl -X POST http://localhost:3333/auth/safe/login \
-H "Content-Type: application/json" \
-d '{"email":"teste@example.com","password":{"$gt":""}}'
```
在这里,Zod 在对数据库进行任何查询之前就会验证请求体,要求 `password` 必须是一个 `string`。像 `{"$gt": ""}` 这样的对象无法通过验证,API 会响应 `400 Bad Request`,根本不会去 MongoDB 中执行查询。
## 🧭 图表:易受攻击 vs. 安全
下面的图表对比了相同的攻击 payload (`password: { "$gt": "" }`) 经过两条路由的过程:
```
sequenceDiagram
participant Cliente
participant Vulneravel as /auth/vulnerable/login
participant Segura as /auth/safe/login
participant Zod
participant MongoDB
Note over Cliente: Payload malicioso
password: { "$gt": "" } rect rgb(255, 230, 230) Cliente->>Vulneravel: POST { email, password: {"$gt": ""} } Vulneravel->>MongoDB: findOne({ email, password: {"$gt": ""} }) Note right of MongoDB: "$gt": "" é sempre verdadeiro,
então qualquer senha "passa" MongoDB-->>Vulneravel: usuário encontrado Vulneravel-->>Cliente: 200 OK (autenticado sem senha real) end rect rgb(230, 255, 230) Cliente->>Segura: POST { email, password: {"$gt": ""} } Segura->>Zod: valida schema (password precisa ser string) Zod-->>Segura: falha na validação Segura-->>Cliente: 400 Bad Request (payload rejeitado) Note over MongoDB: Query nunca chega a ser executada end ``` Zod 在查询之前引入的决策流程图: ``` flowchart TD A[Requisição chega em /auth/safe/login] --> B{password é string?} B -- Não, é objeto/operador Mongo --> C[400 Bad Request
Invalid request payload] B -- Sim --> D[User.findOne com email e password] D --> E{Encontrou usuário?} E -- Sim --> F[200 OK] E -- Não --> G[400 Bad Request
user/pass not found] ``` ## 🛡️ 如何防止 NoSQL Injection - 在使用前验证所有用户输入。 - 在期望原始类型(string, number 等)的地方拒绝对象。 - 在应用边界处使用 schema 验证(Zod, Joi 等)。 - 在将 payload 传递给查询之前对其进行清理。 - 切勿将 `req.body` 直接传递给数据库查询。 - 切勿信任来自客户端的数据。 - 在真实项目中,使用真正的身份验证(token, session, OAuth 等)。 - 在真实项目中,始终对密码进行哈希处理(bcrypt, argon2 等)—— 切勿以明文形式存储。 ## 🔧 现代化决策 - Node 10 → Node 24。 - 移除 Babel —— 项目运行原生 ESM,无需转译。 - 移除 Cluster。 - Express 4 → Express 5。 - Mongoose 5 → Mongoose 8。 - 移除 Sequelize 和 PostgreSQL —— 项目仅通过 Mongoose 使用 MongoDB。 - 移除 Webpack。 - 移除旧的 Husky 配置。 - 将 Jest 和 Supertest 更新到最新版本。 - 为健康检查、用户和身份验证路由添加了集成测试。 - 更新 Dockerfile 以支持 Node 24 并实现无 Babel 构建。 - 添加了 Docker Compose,内置 MongoDB 服务。 - 重写 README 以反映项目的当前状态。 - 通过 GitHub Actions 添加了 CI(在每次 push/PR 时执行 lint、格式检查和测试)。 ## 📁 文件夹结构 ``` . ├── .github │ └── workflows │ └── ci.yml # Pipeline de CI (lint, format check e testes) ├── src │ ├── app │ │ ├── controllers # Regras de cada rota (Health, Users, Auth) │ │ └── schemas # Schemas do Mongoose (dados) e do Zod (validação) │ ├── database # Conexão com o MongoDB │ ├── helpers # Logger e enums (códigos HTTP) │ ├── routes # Definição das rotas por domínio │ ├── app.js # Configuração do Express │ └── server.js # Ponto de entrada da aplicação ├── __tests__ │ ├── integration # Testes de integração (Supertest) │ └── support # Setup do mongodb-memory-server ├── swagger │ └── openapi.yaml # Documentação OpenAPI da API ├── postman │ └── NoSQL Injection.postman_collection.json ├── Dockerfile ├── docker-compose.yml └── .env.example ``` ## ⚙️ CI 位于 `.github/workflows/ci.yml` 的 workflow 会在每次向 `master` 推送以及 pull request 时运行,执行以下操作: 1. `npm ci` 2. `npm run lint` 3. `npm run format:check` 4. `npm test` ## 🗺️ 后续计划 本 POC 范围之外的演进建议: - 添加 bcrypt 以对密码进行哈希处理。 - 添加真实的身份验证(JWT, session 等)。 - 在登录路由上添加 rate limit。 - 改进错误处理(针对不同类型的失败提供更具体的响应)。 - 发布 Swagger 的 HTML 文档。 - 提高 controller 中错误处理块 (`catch`) 的测试覆盖率。 由 [Felps03](https://github.com/Felps03) 制作。如果这个项目帮助你理解了 NoSQL Injection,请在仓库中点个 ⭐。
password: { "$gt": "" } rect rgb(255, 230, 230) Cliente->>Vulneravel: POST { email, password: {"$gt": ""} } Vulneravel->>MongoDB: findOne({ email, password: {"$gt": ""} }) Note right of MongoDB: "$gt": "" é sempre verdadeiro,
então qualquer senha "passa" MongoDB-->>Vulneravel: usuário encontrado Vulneravel-->>Cliente: 200 OK (autenticado sem senha real) end rect rgb(230, 255, 230) Cliente->>Segura: POST { email, password: {"$gt": ""} } Segura->>Zod: valida schema (password precisa ser string) Zod-->>Segura: falha na validação Segura-->>Cliente: 400 Bad Request (payload rejeitado) Note over MongoDB: Query nunca chega a ser executada end ``` Zod 在查询之前引入的决策流程图: ``` flowchart TD A[Requisição chega em /auth/safe/login] --> B{password é string?} B -- Não, é objeto/operador Mongo --> C[400 Bad Request
Invalid request payload] B -- Sim --> D[User.findOne com email e password] D --> E{Encontrou usuário?} E -- Sim --> F[200 OK] E -- Não --> G[400 Bad Request
user/pass not found] ``` ## 🛡️ 如何防止 NoSQL Injection - 在使用前验证所有用户输入。 - 在期望原始类型(string, number 等)的地方拒绝对象。 - 在应用边界处使用 schema 验证(Zod, Joi 等)。 - 在将 payload 传递给查询之前对其进行清理。 - 切勿将 `req.body` 直接传递给数据库查询。 - 切勿信任来自客户端的数据。 - 在真实项目中,使用真正的身份验证(token, session, OAuth 等)。 - 在真实项目中,始终对密码进行哈希处理(bcrypt, argon2 等)—— 切勿以明文形式存储。 ## 🔧 现代化决策 - Node 10 → Node 24。 - 移除 Babel —— 项目运行原生 ESM,无需转译。 - 移除 Cluster。 - Express 4 → Express 5。 - Mongoose 5 → Mongoose 8。 - 移除 Sequelize 和 PostgreSQL —— 项目仅通过 Mongoose 使用 MongoDB。 - 移除 Webpack。 - 移除旧的 Husky 配置。 - 将 Jest 和 Supertest 更新到最新版本。 - 为健康检查、用户和身份验证路由添加了集成测试。 - 更新 Dockerfile 以支持 Node 24 并实现无 Babel 构建。 - 添加了 Docker Compose,内置 MongoDB 服务。 - 重写 README 以反映项目的当前状态。 - 通过 GitHub Actions 添加了 CI(在每次 push/PR 时执行 lint、格式检查和测试)。 ## 📁 文件夹结构 ``` . ├── .github │ └── workflows │ └── ci.yml # Pipeline de CI (lint, format check e testes) ├── src │ ├── app │ │ ├── controllers # Regras de cada rota (Health, Users, Auth) │ │ └── schemas # Schemas do Mongoose (dados) e do Zod (validação) │ ├── database # Conexão com o MongoDB │ ├── helpers # Logger e enums (códigos HTTP) │ ├── routes # Definição das rotas por domínio │ ├── app.js # Configuração do Express │ └── server.js # Ponto de entrada da aplicação ├── __tests__ │ ├── integration # Testes de integração (Supertest) │ └── support # Setup do mongodb-memory-server ├── swagger │ └── openapi.yaml # Documentação OpenAPI da API ├── postman │ └── NoSQL Injection.postman_collection.json ├── Dockerfile ├── docker-compose.yml └── .env.example ``` ## ⚙️ CI 位于 `.github/workflows/ci.yml` 的 workflow 会在每次向 `master` 推送以及 pull request 时运行,执行以下操作: 1. `npm ci` 2. `npm run lint` 3. `npm run format:check` 4. `npm test` ## 🗺️ 后续计划 本 POC 范围之外的演进建议: - 添加 bcrypt 以对密码进行哈希处理。 - 添加真实的身份验证(JWT, session 等)。 - 在登录路由上添加 rate limit。 - 改进错误处理(针对不同类型的失败提供更具体的响应)。 - 发布 Swagger 的 HTML 文档。 - 提高 controller 中错误处理块 (`catch`) 的测试覆盖率。 由 [Felps03](https://github.com/Felps03) 制作。如果这个项目帮助你理解了 NoSQL Injection,请在仓库中点个 ⭐。
标签:CISA项目, GNU通用公共许可证, Maven, MITM代理, MongoDB, Node.js, Web安全, Zod, 安全防护, 漏洞验证, 版权保护, 自定义脚本, 蓝队分析, 请求拦截