flawopen.com/SSRF/Node.js fetch() 与 SSRF
Node.js 的 fetch() 会默认防止 SSRF 吗?
不会。服务器端 fetch() 代表服务器发起网络请求。若调用者能选目标,就可能让服务器访问调用者本身无法触达的系统。就像让可信快递员不核对地址便送件。风险取决于服务器网络位置、身份、URL 策略和重定向行为。
providerId)固定映射到应用预先配置的来源,避免接受任意 URL。根因:服务器信任调用者选择的目标
Node.js fetch() 是通用 HTTP 客户端,不会替应用制定 SSRF 策略;浏览器 CORS 也不会限制服务器可访问的地址。解析 URL 本身不够:允许的主机名可能解析到禁止的 IP,响应也可能重定向到其他主机。用户提供的头像、webhook、文档或预览链接都可能触发这类请求。
请求如何越过信任边界
调用者影响 URL
接口接受完整 URL,或接受足以构造 URL 的数据,例如图片预览地址。
服务器发出请求
HTTP 客户端使用服务器进程可用的网络路由、DNS、代理和身份。
重定向可改变目标
Fetch 默认跟随重定向。最初通过检查的地址可返回 Location,指向本应禁止的主机。
响应可能暴露可达数据
若网络与策略允许,内部服务或云元数据可能可达。是否可利用取决于环境,不是每次 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 防护清单
- ✓尽可能用应用拥有的固定目标映射取代任意 URL,并先验证供应商键。
- ✓仅允许
https:和预期端口;拒绝 URL 凭据、意外主机、IP 字面量和禁止网段。 - ✓用
redirect: "error"禁止重定向;若必须支持,每次连接前都校验目标。 - ✓验证全部 A/AAAA 结果,并让连接固定到已验证地址,同时保留主机名证书校验;重试和代理也须受控。
- ✓通过出站防火墙或代理阻断 loopback、私网、link-local 和元数据网段,包括 IPv6。不要只依赖应用检查。
- ✓限制连接时间、响应字节数和并发;不要转发 Cookie、授权头或云凭据。
- ✓在 AWS 要求 IMDSv2 并限制元数据访问作为纵深防御;这不能替代 URL 和出站网络控制。
常见问题
不够。重定向、IPv4/IPv6 表示形式、DNS rebinding、代理,以及解析到连接之间的竞态都可能绕过简单检查。优先用目标白名单;若允许任意目标,验证每个地址并确保连接使用该结果,同时在网络出口执行同样规则。
不能。IMDSv2 的令牌流程可减少部分风险,但 SSRF 仍可能触达其他内网服务或不同配置下的元数据地址。目标校验和出站限制仍须保留。
一手来源
- OWASP — SSRF 防护备忘单
- Node.js — 全局 fetch API
- WHATWG Fetch 标准 — 重定向模式
- AWS — EC2 实例元数据服务选项
- CWE-918 — Server-Side Request Forgery