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安全, 安全中间件, 数据可视化, 网络请求安全, 自定义脚本, 蓝队分析