Skip to content

v0.0.18 — vision API 中 image_url 的 SSRF 漏洞修复 ​

修复 vision API 中通过 image_url 触发的 SSRF(服务端请求伪造)漏洞:攻击者构造指向内网 / 云元数据地址的图片 URL,可导致云凭据泄露、内网扫描与敏感数据外泄。

中文 ​

🔒 安全 ​

  • 修复 vision API 中 image_url 导致的 SSRF 漏洞(common/image/image.go):
    • GetImageFromUrl 在 HTTP 请求前解析 URL 主机名并拒绝解析到私有 / 保留 IP 段的目标,覆盖 loopback、RFC1918、link-local(云元数据 169.254.169.254)、CGNAT 100.64.0.0/10、IPv6 ULA fc00::/7、IPv6 link-local fe80::/10、IPv4-mapped IPv6 ::ffff:127.0.0.1 等。
    • 同步将裸 http.Get(url) 替换为 client.UserContentRequestHTTPClient.Get(url),统一代理与超时配置,避免 GetImageFromUrl 成为绕过 USER_CONTENT_REQUEST_PROXY 的唯一出口。
    • 仅放行 http / https scheme,其他 scheme(如 file://、gopher://)一律拒绝。
  • 新增 IsPrivateIP(ip net.IP) bool 私有 IP 判定辅助函数(common/network/ip.go):
    • 覆盖 IPv4 loopback / RFC1918 / link-local / CGNAT / 0.0.0.0/8 / 组播 / 保留;IPv6 loopback / ULA / link-local / 组播;IPv4-mapped IPv6。
    • 错误地把 IPv4 输入(如 172.15.255.255)与 IPv4-mapped IPv6 CIDR(::ffff:0:0/96)混淆会导致公网 IP 被误拦,已通过 ip.To4() 分支隔离修复。
  • 新增 IsPrivateIP 单元测试(common/network/ip_test.go):覆盖 loopback、RFC1918、link-local(云元数据)、CGNAT、IPv6 ULA、IPv4-mapped IPv6 的内网段拦截,以及公网 IP 正例与边界(172.15.x / 172.32.x / 100.63.x)的放行。

⚠️ 升级注意事项 ​

  • 零数据库迁移:仅后端逻辑修复,无表结构变更。
  • 后端二进制必须重启:GetImageFromUrl 与 IsPrivateIP 的修改编译进二进制后才生效,升级后必须重启 one-api-pro 进程。
  • 行为变更:
    • 任何调用 vision API(Anthropic / Ollama / Gemini 等 adaptor)并通过 image_url 提交的内网 / 私有地址(含 127.0.0.1、10.x.x.x、192.168.x.x、172.16-31.x.x、169.254.169.254、IPv6 ULA、IPv4-mapped IPv6 等)的请求,将被静默拒绝(沿用项目历史约定,与调用方 _ , _ , _ := image.GetImageFromUrl(...) 错误丢弃语义一致)。
    • 历史数据中如有合法图片源在内网(如自托管 CDN),升级后会被默认拦截。如确有此场景,请联系项目方后续扩展白名单配置(本次未引入配置开关,保持最小改动原则)。
  • 构建与测试:
    • go build ./... 通过。
    • go test ./common/network/... ./common/image/... 全绿(新增 26 个 IsPrivateIP 断言;TestIsIpInSubnet 仍绿)。
  • 影响面:relay/adaptor/anthropic/main.go:136、relay/adaptor/provider/ollama/main.go:48、relay/adaptor/provider/gemini/main.go:118、relay/adaptor/openai/token.go:206 共 4 处调用 GetImageFromUrl / GetImageSize,本次未修改其调用签名,行为对调用方透明(被拦截时仅返回空值与 nil error,调用方按既有静默丢弃语义处理)。