anatolyben/safe-fetch-guard
GitHub: anatolyben/safe-fetch-guard
为 Node.js 提供 DNS 固定的安全 HTTP 请求与 Express 预检中间件,从底层防止 SSRF 攻击和 DNS 重绑定绕过。
Stars: 0 | Forks: 0
# safe-fetch-guard
为 Node.js 中不受信任的出站 URL 提供 DNS 固定且具有限制的 HTTP 请求功能,以及用于 Express 的预检 URL 验证中间件。
## 问题背景
服务端请求伪造(SSRF)发生在 Web 应用程序向用户提供的任意 URL 发起 HTTP 请求时。如果不加限制,攻击者可以迫使你的服务器向内部资源发起请求(例如 AWS/GCP 元数据端点 `169.254.169.254`、环回地址 `127.0.0.1`,或诸如 `10.0.0.0/8` 的内部私有子网)。
像 `fetch` 或 `axios` 这样的标准库默认**不**提供针对此类攻击的保护,而且简单的正则表达式检查通常可以通过 DNS 重绑定(例如,将公共域名解析为 `127.0.0.1`)来绕过。
**`safe-fetch-guard` 通过以下方式解决此问题:**
1. 拒绝错误的 URL 语法和已知的私有 IP。
2. 在调度时解析每个地址,拒绝私有结果,并将实际连接固定到已验证的地址集。
3. 自动中断重定向循环,并验证重定向链中的每一跳。
4. 对传入的响应正文强制执行字节大小限制。
## 安装
```
npm install safe-fetch-guard
```
## 作为工具使用(`safeFetch`)
`safeFetch` 是受保护的调度路径。它特意使用自己的 Undici 传输层,这样调用者就不会意外地用未经验证的 fetch 实现替换掉绑定了 DNS 的连接。
```
import { safeFetch, SafeFetchError } from 'safe-fetch-guard';
try {
// Pass an untrusted webhook or scraping URL safely
const response = await safeFetch('https://example.com/webhook', {
method: 'GET',
timeoutMs: 5000,
maxBytes: 1024 * 1024, // 1MB cap
});
const body = await response.json();
console.log(body);
} catch (error) {
if (error instanceof SafeFetchError) {
console.error('Fetch failed securely:', error.code, error.message);
}
}
```
## 作为 Express 中间件使用
Express 中间件在接受请求之前会检查 URL 语法及其当前的 DNS 解析。这是有用的输入验证,但它无法保护后续独立的 HTTP 客户端调用。当应用程序最终调度该 URL 时,请使用 `safeFetch`。
```
import express from 'express';
import { ssrfMiddleware } from 'safe-fetch-guard';
const app = express();
app.use(express.json());
// Protect the `webhookUrl` field in req.body
app.post('/api/register-webhook', ssrfMiddleware({ bodyFields: ['webhookUrl'] }), (req, res) => {
// Preflight passed. Persist the URL, then use safeFetch when dispatching it.
res.send('Webhook registered!');
});
app.listen(3000);
```
## 功能特性
- **DNS 重绑定保护:** `safeFetch` 将连接查找固定为在每次请求之前立即验证过的确切公共地址集。
- **内部 IP 阻断:** 默认情况下,严格拒绝环回地址(`127.0.0.1`)、链路本地 IP(如 `169.254.169.254`)以及所有私有 RFC1918 网段。
- **重定向验证:** 如果目标服务器发生重定向,`safe-fetch-guard` 会手动检查重定向位置,以确保其没有试图向内部网络进行渗透。
- **凭证隔离:** 当重定向跨域时,会移除 Authorization 和 cookie 标头。
- **有限生命周期:** 超时机制会一直生效,直到响应正文被消费或显式关闭。
- **大小限制:** 在流式传输的同时安全地限制响应正文大小,以防止内存耗尽。
- **可扩展性:** 可以覆盖 user-agent、超时限制,甚至为了测试环境切换 localhost 的允许设置。
## 响应清理
调用 `text()`、`json()` 或 `arrayBuffer()` 会消费限制的正文并释放传输层。如果你故意不消费正文,请调用 `await response.close()`。
## 版本 2
版本 2 移除了可注入的 fetch 实现,因为任意的客户端可能会忽略固定的调度器,并无提示地重新引入 DNS 重绑定。它还将 Undici 设为显式依赖项,并要求 Node.js 20 或更高版本。
## 许可证
MIT
标签:CISA项目, DNS重绑定防御, GNU通用公共许可证, MITM代理, Node.js, SSRF防护, Web安全, 安全中间件, 数据可视化, 网络请求安全, 自定义脚本, 蓝队分析