安全设计 · Runtime 网络
Runtime 出站 URL 与 SSRF 防护
证据快照:2026-08-12,Runtime 实现截至 Task 10 整合修复(Task 1–8 基点 80c377fa)。本文依次介绍 OpenProgram 现状、其他项目和 OpenProgram 后续计划;实现行为与未来工作分别标注。
Runtime 拥有的 HTTP 请求使用注册表键控 URL 策略和托管传输。实现验证全部 DNS 结果,只连接批准的 peer,显式处理重定向,将凭据限制在声明的 origin,限制响应资源,并在发现未分类使用方或活动非托管 SDK 传输时使静态验证失败。
01已实现边界
| 组件 | 已实现职责 | 明确不负责 |
|---|---|---|
openprogram/security/url_policy.py | URL/origin 规范化;scheme、userinfo、host、port 验证;全部 A/AAAA 分类;精确 owner 例外;稳定脱敏 reason code | 不发送网络请求,不推断调用方信任类别 |
openprogram/security/safe_http.py | 不可变的 42 使用方注册表;同步/异步 peer 约束传输;保留 Host/SNI;手动重定向;凭据、header、MIME、解码正文、编码、超时、代理及原子下载约束 | 不接受调用方创建的策略对象,不暴露 verify=False |
| 使用方 adapter | 选择一个不可变注册键;配置服务或回调还提供 owner 配置的精确 origin | 不得另写私网地址分类器或通过原始客户端重试 |
runtime_http_audit.py, doctor以及 scripts/check_runtime_http.py | 有界脱敏拒绝状态、精确 owner 例外/代理报告、AST 清单、SDK 处置验证和过期排除检测 | 不显示 userinfo、query、fragment、凭据、peer 响应正文或含 token 的路径 |
02Runtime 使用方与独立边界
| 边界 | 当前处理 | 本功能中的状态 |
|---|---|---|
| 任意及派生 Runtime URL | web_fetch、附件、生成资产、图片结果下载、目录、市场和更新元数据使用公共或精确服务策略的托管客户端 | 纳入 |
| 固定/配置 API | 网页搜索、图片 API、channel REST、TTS、provider 列表/探测/OAuth、Web 模型列表、MCP HTTP/SSE 和本地探测使用固定或 owner 配置的精确 origin | 纳入 |
| 第三方 SDK | Provider/MCP 客户端注入托管传输;精确 origin REST adapter 保持托管;无法注入的 Bedrock、Slack/Discord gateway 和 edge-tts SDK 路径关闭失败 | 纳入并分类 |
| 浏览器导航、popup、Service Worker 和子资源 | 由浏览器控制设计及 owner 出站隔离管理 | 排除 |
| 沙箱任意代码、插件代码、外部 provider CLI 和包管理器 | 由沙箱、插件信任、子进程和安装边界管理 | 排除 |
| Owner 控制面存活探测 | 仅 loopback 的有限排除按精确调用类型、owner、原因和数量列入清单 | 已分类,不宣称属于 Runtime URL fetch |
◐ 。浏览器、任意代码、插件、外部 CLI 和包管理器子进程仍是独立边界。比较保留功能矩阵全部十二个外部列及 OWASP。权限提示、hostname 规则、应用 URL 验证、策略代理和操作系统网络沙箱是不同控制;上游包装器和协议库不重复计为独立实现。
03参考机制
| 项目 | 应用 URL fetch 证据 | 网络或权限边界 | 采用、调整或拒绝 |
|---|---|---|---|
| Hermes | 共享 url_safety 覆盖 Web、浏览器、vision 和 gateway media。DNS 失败关闭;拒绝 loopback、RFC1918、link-local、CGNAT、metadata、reserved、multicast 和 unspecified 结果;逐跳检查重定向;URL 脱敏,媒体流式处理且有限额。 | 进程级 allow_private_urls 开关同时放宽多个入口。应用检查仍不能替代操作系统网络隔离。 | 采用共享失败关闭验证、完整地址集、重定向检查、脱敏与限额。将全局开关改为精确使用方范围内的服务 origin 例外。 |
| OpenClaw | 默认拒绝私有、内部和特殊用途目标,支持精确/通配 hostname 例外,并在导航、交互引发加载及最终 URL 处检查。文档指出推测导航和浏览器子资源风险。 | 官方文档说明请求拦截不是网络防火墙:重定向、popup、Service Worker 和后台资源仍可能需要出站隔离或策略代理。 | 采用严格默认值和显式例外。浏览器拦截作为独立验收边界,不计为 Runtime fetch 证据。 |
| Claude Code | WebFetch(domain:...) 支持 allow/deny。请求前将 hostname 发到 Anthropic 安全阻止列表,结果缓存精确五分钟;owner 可禁用 preflight。 | Bash 沙箱使用子进程继承的 Seatbelt/bubblewrap HTTP/SOCKS 代理。代理信任客户端 hostname 且不检查 TLS 内容,因此宽泛域名和 domain fronting 仍有风险。 | 采用分层工具授权和子进程出站策略;不将 hostname 权限视为 DNS/IP/连接时 SSRF 证据。 |
| Codex | 审计的主线不以一条 WebFetch 字符串检查替代网络边界。 | codex-network-proxy 提供 HTTP/SOCKS5 代理、精确/受限通配域、私网解析拒绝、有限只读方法、MITM header hook、Unix socket 控制和结构化审计;操作系统沙箱限制 loopback 绕过。 | 采用结构化拒绝原因、审计和托管策略;保留了解使用方的 Runtime 安全客户端,不只依赖子进程代理。 |
| CodeBuddy 2.109.3 | WebFetch 应用安全中心域名策略,拒绝 IP 字面值、 localhost和部分私有范围。企业微信媒体使用官方 CDN allowlist,禁用重定向。 | 沙箱 allowedDomains 与 WebFetch 权限取交集。审计 bundle 的普通 WebFetch 只检查 host 字面值;快照未确认完整 hostname A/AAAA、peer 绑定或逐跳重定向约束。 | 采用固定 channel 服务 allowlist 和权限/网络策略交集;拒绝普通 fetch 仅保护 IP 字面值。 |
| Gemini CLI | 官方工具文档警告 web_fetch 能访问 localhost/私网;Plan Mode 可要求确认。本地 0.35.3 core 含 IP 分类与 DNS 工具,但该警告表明它们不构成通用 WebFetch 私网边界。 | 用户确认是权限决策,不限制实际连接 peer。 | 采用明确风险文档;拒绝以确认替代确定性目标约束。 |
| OpenCode | webfetch 权限使用完整请求 URL,支持 allow、ask、deny。当前官方权限文档不能证明私网 IP 分类、完整 DNS、逐跳重定向或 peer 绑定。 | shell 使用主机用户网络权限;官方说明字符串规则是尽力处理,不是网络隔离。 | 采用统一资源权限语义;获准 URL 仍必须通过独立传输策略。 |
| pi-mono | 固定快照未确认核心产品内置任意 URL fetch;网络能力主要来自 shell、安装器或用户扩展。 | 核心快照未确认 OS 出站策略;第三方 pi-sandbox 不计为内置 pi-mono 行为。 | 不作为 Runtime SSRF 算法参考;扩展边界支持将第三方代码排除在安全客户端完成范围外。 |
| pi-ai | 这是协议参考源码,不是可运行产品,没有 session 或 Runtime URL 使用方。 | 不适用。 | 排除产品级 SSRF 结论,但保留矩阵列以维护审计单位区别。 |
| weclaw | 固定快照是到外部 ACP/CLI Agent 的消息桥接,未确认独立通用 URL fetch。 | 主要继承启动 CLI 的工具和网络权限;不将受委托产品的控制重复算给 weclaw。 | 保留 channel media 和外部进程启动作为相关边界;不作为通用安全 HTTP 参考。 |
| cc-internal / cx-internal / gm-internal | cc-internal 和 cx-internal 包装上游启动器; gm-internal 大量复用 Gemini CLI。审计包未确认独立加强通用 URL fetch 策略。 | 继承大部分 Claude Code、Codex 或 Gemini CLI 的权限和网络行为。增加认证或 endpoint 本身不构成 SSRF 边界。 | 仅审计其新增 endpoint、凭据转发和配置差异;不重复计算为三个独立安全设计。 |
| OWASP | 区分已知服务 allowlist 与任意公共目标验证,限制协议,验证全部 A/AAAA,可信服务优先使用 allowlist。 | 单次 DNS preflight 仍有 DNS pinning 和检查/使用时差风险,因此也建议网络控制。 | 采用独立信任类别和连接时 peer 约束;拒绝只靠单个 preflight DNS 结果。 |
一手来源: Hermes url_safety.py, Hermes 安全, OpenClaw 安全, Claude Code 沙箱, Claude Code WebFetch 安全检查, Codex 网络代理, OpenCode 权限, Gemini CLI 工具以及 OWASP SSRF 防护.
固定版本本地快照证据: @tencent-ai/codebuddy-code@2.109.3 dist/codebuddy.js,
CHANGELOG.md, dist/web-ui/docs/cn/cli/permissions.md; @google/gemini-cli@0.35.3 core/dist/src/utils/fetch.js;
references/pi-mono@3eb002766fff; references/weclaw@10711d0c6b7e;以及 references/pi-ai.
04采用、调整与拒绝的选择
| 决策 | 机制 | 原因 |
|---|---|---|
| 采用 | 一个纯 URL/IP 策略;全部 DNS 地址通过;DNS 失败关闭;每次重定向重新判断;错误只含 origin;限制解码正文 | 统一覆盖 Runtime 的目标、重定向、凭据与资源边界。 |
| 采用 | 区分不可信公共、固定公共服务、配置服务及 loopback 回调四种信任类别 | 保留 owner 配置的 localhost/私网服务可用性,不给任意公共使用方私网权限。 |
| 采用 | Claude Code/CodeBuddy 权限与网络分层、Codex 结构化拒绝原因、CodeBuddy 固定 channel media allowlist | 权限回答谁可请求资源;传输策略回答哪个 peer 接收连接与凭据。 |
| 调整 | Hermes 进程级 allow_private_urls | 替换为精确使用方加规范 origin 或规范私有/本地 CIDR;metadata 和 link-local 仍不可例外。 |
| 调整 | OpenClaw 浏览器拦截与 Codex 全进程出站代理 | 浏览器控制保持独立门禁;策略代理是可选、owner 声明、独立 peer 约束且 doctor 可见,不允许直接 fallback。 |
| 拒绝 | 仅 URL 字符串检查、单次 DNS preflight、隐式重定向、继承环境代理、禁用 TLS 验证或原始客户端 fallback | 每项都留下目标、重绑定、凭据或绕过路径。 |
| 拒绝 | 以用户确认或 hostname allow/deny 实现目标约束 | 两者均不验证全部解析地址,也不约束连接 peer。 |
| 拒绝 | 对所有 HTTP 客户端实施仅公共目标规则 | 这会破坏显式配置的本地模型 endpoint、MCP server 和内网服务。 |
| 拒绝 | 将 Origin/Host、CORS、owner 认证、浏览器检查或沙箱网络算作 Runtime SSRF 完成 | 这些控制保护不同方向或执行边界。 |
Runtime URL fetch 截至 Task 8 已完成。本节先记录实现证据,再列独立后续边界。矩阵保持 ◐ ,因为这些独立边界不在范围内。
05已完成计划:实现证据
以注册表键标识的调用方
四种信任类别
精确配置或回调 origin
纯策略决策
一组完整 DNS 结果
精确例外与 reason code
托管传输
原始 Host、SNI 和证书
手动重定向与有界响应
05.1 传输与策略行为
- 公共请求仅允许注册表声明的方法、scheme 和 port;全部地址必须为 global,显式拒绝 metadata、loopback、private、link-local、CGNAT、multicast、reserved、unspecified、文档/测试、映射、歧义和无效目标。
- 同步与异步
httpcorebackend 只连接批准的 IP 元组,验证实际 socket peer,保留规范 hostname 用于 HTTP Host、TLS SNI 与证书验证。连接池身份包含使用方、信任类别、规范 origin、批准 IP 和代理身份。 - 手动重定向且每跳重新决策;跨 origin 配置服务重定向失败,公共请求凭据被移除,拒绝 URL userinfo;日志/公开错误不含 query、fragment、token 路径、cookie 或 peer 控制正文。
- 限制 header、MIME、content encoding、解码字节、connect/read/write/pool/overall 超时和临时文件生命周期。下载仅在完整验证、flush、
fsync及可安全清理的完成之后替换目标。
05.2 使用方与 SDK 处置证据
| 处置 | 使用方 | 约束 |
|---|---|---|
| 注入传输 | provider.openai.sdk, provider.anthropic.sdk, provider.google.sdk, mcp.configured.http, mcp.configured.sse | 公开 SDK 构造接收限定范围的托管客户端或工厂;真实 SDK 链测试到达受约束 backend。 |
| 精确 origin | Telegram、WeChat、Feishu、Matrix、Slack、Discord、固定/配置 TTS 和其他固定/配置 REST 使用方 | 附加凭据前冻结规范 owner/随附 origin;重定向不能把凭据转到其他 origin。 |
| 派生 URL 提取 | 附件、生成资产和图片结果使用方 | SDK/上游响应提供 URL,随后 Runtime 通过专用托管注册使用方下载或上传。 |
| 禁用 | provider.amazon_bedrock.sdk, channel.slack.gateway_sdk, channel.discord.gateway_sdk, tts.edge_sdk | 启动或调用以 UNMANAGED_TRANSPORT 失败,发生在导入/使用无法注入网络 SDK 前。Bedrock 同时覆盖注册 stream 和模型列表入口,早于 boto3/botocore 凭据或客户端活动。 |
05.3 Owner 例外、策略代理、doctor 和清单
Owner 配置只接受注册使用方加一个规范 origin 或规范私有/本地 CIDR。拒绝通配、后缀、凭据、未知字段、公共/默认路由网段、metadata、link-local 和使用方不允许的例外形式。既有 owner 认证设置路径是唯一变更权限。
只有 owner 配置设置 enforces_target_policy: true才可用策略代理。Runtime 先判断目标,独立约束代理 peer,通过 doctor/audit 报告委托,绝不直接重试。
| 清单排除 | Owner 与限定原因 |
|---|---|
security/safe_http.py | Runtime 托管传输;唯一允许的直接 httpcore pool/proxy 构造 |
programs/tools/web/browser/_chrome_bootstrap.py | 浏览器控制;精确 browser opener 构造和 loopback CDP 存活连接 |
cli/commands/mcp.py | Owner 控制面;仅认证 backend opener |
cli/ink.py, cli/commands/doctor.py, cli/commands/rescue.py | Owner 控制面;仅精确 loopback worker 存活 socket |
未注册 raw 调用、活动非托管 SDK、没有分类使用方的注册项或过期排除均使检查失败。Doctor 暴露稳定的 runtime-http-registry, runtime-http-owner-exceptions,
runtime-http-policy-proxy, runtime-http-recent-denials以及 runtime-http-unmanaged-transport 标签。
05.4 验证快照
| 命令 | 记录结果 |
|---|---|
uv run --extra dev pytest -q tests/security | 804 passed |
uv run --with pip --with boto3 pytest -q tests/meta_functions tests/providers tests/unit tests/webui tests/integration/programs/test_mcp_client.py | 3482 passed, 12 skipped, 1 xfailed |
uv run --extra dev --with boto3 pytest -q | 4923 passed, 14 skipped, 2 deselected, 1 xfailed |
uv run python scripts/check_runtime_http.py | unregistered=0 active_unmanaged=0 registry_without_consumer=0 stale_exclusions=0 |
uv run python scripts/check_feature_matrix.py docs/reference/design/feature-matrix.html | features=160 openprogram_score=78 openprogram_gaps=73 openprogram_only=6 |
◐ ,因为验证范围是 Runtime 拥有的 HTTP,不包括独立浏览器、任意代码、插件、外部 CLI 和包管理器边界。06OpenProgram 后续计划
| 后续边界 | 宣称完成前必需工作 |
|---|---|
| 浏览器导航及页面派生流量 | 为每个 backend 定义/测试导航、popup、Service Worker、后台资源、重定向、下载、CDP 和浏览器 profile 策略;拦截不足时使用强制出站边界。 |
| 插件与任意代码网络 | 信任/沙箱模型与 Runtime 安全 HTTP 独立;如需限制这些进程网络,增加 OS 或策略代理出站边界。 |
| 禁用的第三方 SDK 网络功能 | 仅在 SDK 支持托管传输注入或 owner 强制策略代理具有同等测试后启用;不得用未验证 SDK 保证替代禁用处置。 |