FlawOpen

flawopen.com/SSRF/Node.js fetch() 与 SSRF

Node.js 的 fetch() 会默认防止 SSRF 吗?

CWE-918: Server-Side Request ForgeryNode.js 安全参考
通俗解释

不会。服务器端 fetch() 代表服务器发起网络请求。若调用者能选目标,就可能让服务器访问调用者本身无法触达的系统。就像让可信快递员不核对地址便送件。风险取决于服务器网络位置、身份、URL 策略和重定向行为。

SSRF(CWE-918)服务器向不可信调用者选择或影响的目标发出请求,越过预期网络信任边界。
目标白名单把用户选项(如 providerId)固定映射到应用预先配置的来源,避免接受任意 URL。
重定向校验响应可指向第二个目标。自动跟随重定向会绕过仅检查初始 URL 的规则。
DNS rebinding同一主机名可解析到不同地址。只检查一次 DNS、连接时又重新解析会造成 TOCTOU 窗口。

根因:服务器信任调用者选择的目标

Node.js fetch() 是通用 HTTP 客户端,不会替应用制定 SSRF 策略;浏览器 CORS 也不会限制服务器可访问的地址。解析 URL 本身不够:允许的主机名可能解析到禁止的 IP,响应也可能重定向到其他主机。用户提供的头像、webhook、文档或预览链接都可能触发这类请求。

请求如何越过信任边界

Step 1

调用者影响 URL

接口接受完整 URL,或接受足以构造 URL 的数据,例如图片预览地址。

Step 2

服务器发出请求

HTTP 客户端使用服务器进程可用的网络路由、DNS、代理和身份。

Step 3

重定向可改变目标

Fetch 默认跟随重定向。最初通过检查的地址可返回 Location,指向本应禁止的主机。

Step 4

响应可能暴露可达数据

若网络与策略允许,内部服务或云元数据可能可达。是否可利用取决于环境,不是每次 fetch 都会如此。

Node.js 中有风险与更安全的写法

有风险
// 不要获取调用者提供的任意 URL。
const response = await fetch(userProvidedUrl);
更安全
// 调用者只能选键,不能选择网络目标。
const destinations = new Map([
  ["status", new URL("https://status.example/")],
  ["docs", new URL("https://docs.example/")],
]);

const target = destinations.get(providerId);
if (!target) throw new Error("Unknown provider");

const response = await fetch(target, {
  redirect: "error",
  signal: AbortSignal.timeout(3000),
});
if (!response.ok) throw new Error("Upstream request failed");

若产品必须接受用户提交的 URL

优先让用户选择供应商 ID,再从固定目标映射构造请求。若必须接受任意公网 URL,仅允许必要的协议和端口;拒绝 user-info、loopback、私网、link-local 及禁止的 IPv4/IPv6 地址;检查所有 DNS 解析结果,并确保连接使用已验证 IP,不再进行未经检查的解析。禁用自动重定向或逐跳验证。另用出站网络策略封锁元数据地址,包括 169.254.169.254 和启用的 IPv6 地址。限制连接时间、响应大小和并发;不要转发内部凭据。

SSRF 防护清单

常见问题

阻止 localhost 和私有 IP 就够了吗?

不够。重定向、IPv4/IPv6 表示形式、DNS rebinding、代理,以及解析到连接之间的竞态都可能绕过简单检查。优先用目标白名单;若允许任意目标,验证每个地址并确保连接使用该结果,同时在网络出口执行同样规则。

启用 IMDSv2 能消除 SSRF 吗?

不能。IMDSv2 的令牌流程可减少部分风险,但 SSRF 仍可能触达其他内网服务或不同配置下的元数据地址。目标校验和出站限制仍须保留。

一手来源

浏览相关安全主题与事件分析:完整安全目录 →
另请参阅 服务器端请求伪造(SSRF,CWE-918):原理以及在各语言中的防御方法 — flawopen.com SSRF 与云元数据:凭据风险和防护措施 AI PR 机器人:防止 SSRF — flawopen.com