Ray 2.58.0 于 8 月 23 日 05:42 UTC 发布。在 Ray Serve 的修复列表中,有一条简短说明:修复 Serve 副本 ASGIService 绕过令牌认证的问题。此次没有附带安全通告,没有申请 CVE,也没有给出严重性等级。
拉取请求说明了什么
PR #65189 比变更日志明确得多。Serve 副本中的内部 gRPC 服务器“是用原始的 grpc.aio.server() 构建,并通过 add_insecure_port 绑定的”,作者称其为 Ray 中唯一一个同时跳过 Ray 两项传输控制的 Python 侧 gRPC 服务器。在 RAY_AUTH_MODE=token 下,该服务会接受缺少有效凭据的请求,并在执行任何校验之前就进入 pickle.loads(request.pickled_request_metadata)。在 RAY_USE_TLS=1 下,Ray 其他 Python gRPC 服务器会使用 mTLS,而这个端口则一直保持未加密。
在校验前就到达 pickle.loads,才是整个问题
反序列化攻击者提供的 pickle,本质上就是按设计执行任意代码——Python 自己的文档也明确说明了这一点。这里关键在于顺序:不受信任的负载先被反序列化,认证却放在后面,这意味着在这条路径上,认证并非只是薄弱,而是完全失去作用。
失真之处:不是一个新漏洞,而是持续五个版本的缺口
Ray 在 2.52.0 中引入了据称覆盖所有 Ray 组件的令牌认证。但这个端口并未被覆盖。任何仅依据该版本说明而启用 RAY_AUTH_MODE=token 的用户,自那以后在每个 Serve 副本上运行的都是一个未认证的反序列化端点。此次修复并非新增保护,而是缩小了文档所述与实际情况之间的差距。
修复比发布早 18 天
PR #65189 于 8 月 5 日合并。它在 8 月 23 日、也就是 2.58.0 发布时才交到运维人员手中。在这 18 天里,这个补丁一直清晰可见地留在公开仓库中,而没有任何一个发布版本包含它——这正是在公开环境下、没有 embargo 的情况下修复安全问题的常规代价,也解释了为什么变更日志中的那一行,比它所在的位置更重要。
运维人员应检查什么
是否暴露取决于网络可达性,因为该端口会在一个临时端口上跨接口绑定。真正需要关注的是那些 Serve 副本部署在可被不受信任调用方访问的网络中的集群。升级后,客户端和服务器都会切换到 Ray 共享的 gRPC 辅助组件,后者会应用 Ray 其余部分已经在使用的拦截器和 TLS 设置。
