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)、CGNAT100.64.0.0/10、IPv6 ULAfc00::/7、IPv6 link-localfe80::/10、IPv4-mapped IPv6::ffff:127.0.0.1等。- 同步将裸
http.Get(url)替换为client.UserContentRequestHTTPClient.Get(url),统一代理与超时配置,避免GetImageFromUrl成为绕过USER_CONTENT_REQUEST_PROXY的唯一出口。 - 仅放行
http/httpsscheme,其他 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()分支隔离修复。
- 覆盖 IPv4 loopback / RFC1918 / link-local / CGNAT /
- 新增
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),升级后会被默认拦截。如确有此场景,请联系项目方后续扩展白名单配置(本次未引入配置开关,保持最小改动原则)。
- 任何调用 vision API(Anthropic / Ollama / Gemini 等 adaptor)并通过
- 构建与测试:
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,本次未修改其调用签名,行为对调用方透明(被拦截时仅返回空值与nilerror,调用方按既有静默丢弃语义处理)。