Hotdry.

Article

Agent HTTP 工具出站 SSRF 网关:解析、解析后校验与 connect 前 IP 裁决

结合 OWASP SSRF 防护要点与 Agent「模型选 URL」场景,说明为何仅做 hostname 黑名单不够,并给出 scheme 白名单、重定向禁用、自定义 Dial 在解析 IP 后拦截 RFC1918/链路本地/元数据地址的可落地参数表。

2026-07-04security

当 Agent 通过 fetch_urlhttp_get 或 MCP 工具向模型或用户提供的 URL 发起服务端请求时,攻击面与传统「Webhook 回调」相同,但触发路径更隐蔽:提示注入或恶意网页可把目标改成 http://169.254.169.254/latest/meta-data/,或利用重定向、非常规 IP 字面量绕过字符串黑名单。OWASP 将 SSRF 定义为攻击者操纵服务端所请求的 URL,从而读取云元数据、内网 HTTP 服务或本地文件(file:// 等)。Agent 平台若把 HTTP 工具直接绑在运行时的默认 http.Client / fetch 上,等价于把内网探测能力交给不可信输入。本文只讨论出站 HTTP (S) 工具的 egress 网关,不覆盖浏览器侧 DNS rebinding 打本地 MCP(该场景见 MCP 传输规范中的 Origin 与绑定地址要求)。

问题背景:Agent 场景放大了「开放 URL」模式

典型 Agent 工具链中,HTTP 客户端由平台进程发起,凭据(云 IAM、内网 API Key、kubeconfig 可达的服务)挂在同一网络命名空间。与「仅允许回调到注册域名」的 SaaS Webhook 不同,许多 coding /research Agent 故意允许访问任意公网 URL 以抓取文档,于是防御必须从 Case 2(任意目标) 出发,再叠加租户级策略(OWASP SSRF Prevention Cheat Sheet 对两类场景分开描述)。

Agent 特有的放大因素包括:

  • 调用方不可信:URL 可能来自 LLM 输出、用户粘贴或远程 MCP Server 的 tools/call 参数,而非产品后台配置。
  • 重试与多步:编排器在 429/5xx 时重试同一 URL,或工具链连续访问重定向链,若不在客户端禁用或严格限制重定向,第一轮通过的 hostname 校验可在 302 后被绕过。
  • 解析与连接分离:仅在校验阶段对 hostname 做 DNS 查询并检查 IP,与实际 connect() 使用的地址若存在时间窗,可能撞上 DNS rebinding(Cheatsheet 在 Domain name 一节专门提示「DNS pinning」类风险)。
  • 非常规 URL 表示:十进制 / 八进制 / 十六进制 IP、user@host、IPv4-mapped IPv6(::ffff:127.0.0.1)等可导致「字符串黑名单未命中但内核连到 loopback」的解析差分(Cheatsheet 在 IP address 一节引用多种 encoding bypass 研究)。

因此,可落地的模式是:独立 egress 代理 / 库,在应用进程与公网之间统一做「解析 → IP 裁决 → 连接」,业务工具禁止自带裸 http.Get

可落地实现:分层策略与推荐参数

1. Scheme 与方法白名单

生产建议 说明
允许 scheme https;开发环境可加 http 但须同样走 IP 裁决 禁止 filegopherdictftpdata 等(OWASP Overview 列举 file:// 读本地风险)
HTTP 方法 GETHEAD;需 POST 时单独工具并限 Content-Type 减少对内网 REST 写接口的滥用面
默认端口 省略端口时 https→443、http→80;显式端口须在 1–65535 拒绝 :0、负端口

2. URL 解析:规范化后再裁决

  1. 使用语言标准库或 WHATWG URL 解析器得到规范 host(勿仅用正则匹配 http:// 前缀)。
  2. 若 host 为 IP 字面量,用正规 IP 解析库(Cheatsheet 强调应使用库输出值做比较,避免 hex/octal 绕过)映射到 net.IP
  3. 若 host 为域名,进入 DNS 解析步骤;不要只做子串黑名单(如 contains("localhost"))。

可选硬规则:拒绝 host 为空、拒绝用户名嵌入(http://evil@127.0.0.1 类 userinfo 混淆)、拒绝非 UTF-8 host(Punycode 由解析库统一处理)。

3. IP 裁决:deny 特殊用途与私网(Case 2 基线)

每次建立连接前(自定义 DialContext / connect hook),对将使用的每一个解析结果执行检查。建议默认拒绝(与 IANA IPv4 Special-Purpose 及常见云文档一致,可按环境收窄):

范围 / 地址 处置
127.0.0.0/8::1/128 拒绝
10.0.0.0/8172.16.0.0/12192.168.0.0/16 拒绝
169.254.0.0/16(含 169.254.169.254 链路本地元数据) 拒绝
0.0.0.0/8100.64.0.0/10(CGNAT) 生产拒绝
IPv4-mapped IPv6 先 unwrap 再判
未全球单播的 IPv6(ULA fc00::/7、link-local fe80::/10 等) 拒绝

元数据服务:OWASP SSRF Overview 以 AWS http://169.254.169.254/ 为例说明配置与密钥泄露风险;网关应默认阻断该网段,不依赖「我们不在 AWS」—— 同网段在其它环境也常作 link-local 服务。

租户若必须访问固定内网 API,应走 Case 1 allowlist(精确域名 + 定期解析的 IP 集合或独立 VPC 连接器),而不是关闭整块 deny 规则。

4. 重定向、响应体与超时

建议值 说明
跟随重定向 生产默认 0(不跟随) Cheatsheet 明确建议禁用客户端重定向以防绕过校验;若业务必须跟随,则每一跳重新执行完整「解析 + IP 裁决」
最大重定向跳数 若开启,≤ 3 与上条联用
单次请求超时 连接 5s、TLS 握手 5s、首字节 30s、整包 60s 防止慢连接拖住 Worker
响应体上限 2–8 MiB(按工具可调) 降低「大文件拖内存」与隐蔽 exfil 通道
并发出站 每 Agent run ≤ 6、每租户全局槽位 与 LLM 工具步数配额联动

5. 与 MCP / 工具幂等层的衔接

HTTP 工具适配器应在调用 egress 网关前写入审计字段:tool_call_idrun_id、最终 URL(规范化后)、裁决结果(允许 / 拒绝及规则 id)。拒绝时向模型返回稳定错误码(如 egress_ssrf_blocked),避免把内部 IP 列表写进错误文案。

与幂等 dedup 层(同一 tool_call_id 重试)配合时,若第一次请求已被拒绝,重试不应换用「更宽松」策略;若第一次成功拉取内容,重试应命中缓存而非再次出站。

6. 参考实现骨架(Go http.Transport

下列逻辑展示「在 Dial 前对 host:port 解析并裁决」,生产需补全 TLS 配置、HTTP/2 与观测性:

func safeTransport(resolver *net.Resolver) *http.Transport {
	dialer := &net.Dialer{Timeout: 5 * time.Second, KeepAlive: 30 * time.Second}
	return &http.Transport{
		DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
			host, port, err := net.SplitHostPort(address)
			if err != nil {
				return nil, err
			}
			ips, err := resolver.LookupIPAddr(ctx, host)
			if err != nil {
				return nil, err
			}
			for _, ipa := range ips {
				if isBlocked(ipa.IP) {
					return nil, fmt.Errorf("egress blocked for %s", ipa.IP)
				}
			}
			return dialer.DialContext(ctx, network, net.JoinHostPort(host, port))
		},
		// 生产:DisableKeepAlives 视情况;重定向在 http.Client CheckRedirect 返回 ErrUseLastResponse
	}
}

isBlocked 应基于 net.IP 的 CIDR 表(含 mapped IPv6 归一化),而非字符串前缀匹配。

风险与边界

DNS rebinding / TOCTOU:校验时 IP 为公网、连接时变为内网,在短 TTL 下仍可能发生。缓解包括:禁用重定向、缩短解析到连接间隔、使用自定义 resolver 并限制可接受 TTL 下限、对高敏租户强制 Case 1 域名 allowlist。完全消除需网络层 egress firewall 与策略路由,超出单进程网关范围。

合法公网目标上的数据泄露:SSRF 网关只解决「打内网 / 元数据」,不阻止 Agent 向攻击者控制的公网服务器 POST 会话片段。需另配 DLP、响应脱敏与出站域名信誉策略。

HTTPS 与 SNI:IP 裁决通过不代表证书可信;应使用系统 CA 池、启用 TLS 1.2+、校验 ServerName 与 URL host 一致,避免误连蜜罐。

Allowlist 维护成本:Case 1 下 DNS 轮询会使「解析一次放行」失效,需定期重解析或在 Service Mesh 使用固定 Egress 名称。

协议走私:仅禁 HTTP (S) 不能阻止其它工具(SMTP、Redis URL 等)若被误暴露;工具注册表应声明网络能力,默认关闭非 HTTP 出站。

IPv6 双栈:仅裁决 IPv4 而忽略 AAAA,会导致通过 IPv6 绕过。应对所有记录类型结果取并集后再判。

参考来源

security

内容声明:本文无广告投放、无付费植入。

如有事实性问题,欢迎发送勘误至 i@hotdrydog.com