当 Agent 通过 fetch_url、http_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 裁决 |
禁止 file、gopher、dict、ftp、data 等(OWASP Overview 列举 file:// 读本地风险) |
| HTTP 方法 | GET、HEAD;需 POST 时单独工具并限 Content-Type |
减少对内网 REST 写接口的滥用面 |
| 默认端口 | 省略端口时 https→443、http→80;显式端口须在 1–65535 | 拒绝 :0、负端口 |
2. URL 解析:规范化后再裁决
- 使用语言标准库或 WHATWG URL 解析器得到规范 host(勿仅用正则匹配
http://前缀)。 - 若 host 为 IP 字面量,用正规 IP 解析库(Cheatsheet 强调应使用库输出值做比较,避免 hex/octal 绕过)映射到
net.IP。 - 若 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/8、172.16.0.0/12、192.168.0.0/16 |
拒绝 |
169.254.0.0/16(含 169.254.169.254 链路本地元数据) |
拒绝 |
0.0.0.0/8、100.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_id、run_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 绕过。应对所有记录类型结果取并集后再判。
参考来源
- OWASP — Server Side Request Forgery (SSRF) — SSRF 定义、元数据与
file://风险概述 - OWASP Cheat Sheet Series — Server-Side Request Forgery Prevention — Case 1/2、解析器 bypass、重定向与 DNS 校验注意事项
- OWASP Gen AI — LLM06:2025 Excessive Agency — 过度权限工具与自主行动的风险框架(HTTP 工具属高敏能力)
- RFC 9110 — HTTP Semantics (Redirection) — 3xx 重定向语义,支撑「每跳重验或禁用跟随」策略
- IANA — IPv4 Special-Purpose Address Registry — 特殊用途地址块权威列表
- AWS Documentation — Instance metadata and user data —
169.254.169.254元数据端点行为说明 - Model Context Protocol — Security Best Practices — 传输与 DNS rebinding 相关部署建议(与出站 HTTP 互补)
内容声明:本文无广告投放、无付费植入。
如有事实性问题,欢迎发送勘误至 i@hotdrydog.com。