系统:运行中
← 返回所有攻击
INFRASTRUCTURE CRITICAL

BadHost(CVE-2026-48710):Host 头中一个字符即可绕过 Starlette、vLLM 与 FastMCP 的鉴权

X41 D-Sec 于 2026 年 5 月 22 日披露 Starlette < 1.0.1 中的关键鉴权绕过。HTTP Host 头中仅插入一个 /、? 或 # 字符,即可使实际路由的路径与中间件看到的路径产生不一致,导致 vLLM、LiteLLM、FastMCP 及成千上万基于 FastAPI 的 AI 智能体的基于路径的授权失效。

2026-05-27 // 8 分钟 affects: starlette, fastapi, vllm, litellm, fastmcp, mcp-servers, text-generation-inference

这是什么?

2026 年 5 月 22 日,德国安全公司 X41 D-Sec 公开披露了 CVE-2026-48710,代号 BadHost:Python ASGI 框架 Starlette 中的一处未认证授权绕过漏洞,而 Starlette 正是 FastAPI 的路由核心。该漏洞最早于 2026 年 1 月 27 日在一次由 OSTIF 资助的 vLLM 推理服务审计中被发现,2 月 4 日通报给 Starlette 维护者,并在 2026 年 5 月 21 日通过 Starlette 1.0.1 修复——仅在公开披露前一天发布。

Starlette 每周下载量约为 3.25 亿次。通过 FastAPI,它支撑了现代 AI 基础设施栈中很大一部分:vLLMLiteLLMText Generation Inference、OpenAI 兼容的 API 代理、MCP 服务器(包括 FastMCP)、智能体控制面以及各种模型服务监控面板。所有从 0.8.3 到 1.0.0(含)的版本均受影响。

漏洞本身极小,影响半径却极大,补丁与披露之间的窗口期实际上为零。

工作原理

Starlette 通过将 HTTP Host 头与请求路径拼接,再将结果重新解析为 URL,来重建 request.url。在拼接之前,Host 的值并未按照 RFC 9112 / RFC 3986 的语法进行校验。客户端在 Host: 中发送的任何字节,都会直接流入 URL 解析器。

如果攻击者在 Host 头中注入 /?#,在重新解析时,authority、path、query 和 fragment 之间的边界就会发生偏移。路由器看到的路径(来自 ASGI scope,源自 HTTP 请求行)与中间件看到的路径(request.url.path)出现分歧。中间件基于路径做出的任何授权决策,实际上是在一条与路由器派发对象不同的字符串上进行的。

# 基于 2026 年 5 月 22 日公开披露内容的概念示意。
# 此处不复现任何针对生产系统的载荷。

线缆: GET /admin HTTP/1.1
      Host: foo?

ASGI scope(事实):
  scope["path"]            = "/admin"     # 路由器在此派发

Starlette 的 URL 重建:
  request.url              = "http://foo?/admin"
  request.url.path         = "/"          # 中间件看到的是这个

中间件:
  if request.url.path.startswith("/admin"):  # False
      require_auth()
  # 鉴权被跳过

路由器:
  dispatch("/admin")                         # handler 仍然执行

最终效果是:一条本应在受保护路由上返回 403 Forbidden 的请求,只要在 Host 头后追加一个非法字符,就会返回 200 OK,而受保护的 handler 依旧执行。

该模式具有通用性。无论中间件来自第三方库还是自己实现的 BaseHTTPMiddleware,关键在于它在做安全决策时读取的是 request.url.path(被污染)还是 scope["path"](真实路径)。

为什么重要

在 AI 安全的语境下,BadHost 有三点尤其值得警惕。

第一,级联效应。Starlette 并不是一个边缘依赖。FastAPI 构建在它之上,而 2025-2026 年的 Python 模型服务和智能体控制面,FastAPI 几乎是事实上的框架。X41 与 OSTIF 明确把 vLLM、LiteLLM、OpenAI 兼容代理和 MCP 服务器列在受影响项目之中。任何运行自托管推理端点、内部 LLM 网关或基于路径 ACL 的 FastMCP 服务的人,在打补丁并完成审计之前都必须假设自己存在漏洞。

第二,MCP 服务器尤其暴露。Model Context Protocol 规范要求提供未认证的 OAuth 发现端点;这些路由可以稳定地、无需凭证地被访问,而它们与本应受到认证保护的管理路由比邻而居。这种布局是一个教科书级的场景:授权绕过可以通过中间件混淆,从一条公开路由”跳板”到一条受保护路由上。

第三,披露时间线。漏洞在 2 月初通报上游,补丁在 3 月初提出,但修复直到 5 月 21 日才发布,X41 第二天就放出了全部细节——运营方实际可用的窗口期是以小时计,而非通常的几天到几周。对于嵌入如此广泛的库而言,这个窗口异常紧迫,防御方应当假设扫描与机会性利用已经在进行中。

防御

缓解路径很短,也很具体。

将 Starlette 升级到 1.0.1 或更高版本。 这是最低限度的措施,直接修复底层的解析问题。FastAPI、vLLM、LiteLLM、FastMCP 以及大多数智能体框架都对 Starlette 存在传递性依赖;需要锁定该依赖并重建容器镜像。补丁会按照 RFC 9112 / 3986 的语法校验 Host,并在值非法时回退到 scope["server"]

将自定义中间件中的安全决策从 request.url.path 切换到 scope["path"] ASGI scope 中的 path 来自 HTTP 请求行,无法通过 Host 注入被污染。这才是架构层面正确的修复,应当能够超越任何单个 CVE 的生命周期:绝不把基于客户端可控头部派生出的值,用于任何授权判断。

立即审计你自己的中间件,包括所有 BaseHTTPMiddleware 的子类以及栈中的原始 ASGI 中间件。X41 在 badhost.org 提供了免费的远程扫描器,并在 GitHub 上发布了用于静态检测漏洞模式的 Semgrep 规则与 CodeQL 查询。请对每一个内部 FastAPI 服务、模型服务器和 MCP 主机都跑一遍。

在反向代理层增加一道兜底防线。 配置 NGINX、Envoy、Traefik 或入口控制器,拒绝任何 Host 头中包含 /?#、空白字符或 RFC 9112 authority 语法以外字节的请求。这是一道有用的”安全带加上吊带”措施,可用于抵御这一类问题的下一种变体。

把 MCP / FastMCP 服务器视为生产环境的攻击面。 如果 MCP 主机与管理端点共存,应将其隔离到独立的网络平面,在管理路由上强制要求客户端证书或 mTLS 认证,不要仅依赖路径前缀进行授权。MCP 规范强制存在的未认证发现端点,从设计上就让基于路径的 ACL 变得脆弱。

对整个问题类别重做测试。 BadHost 属于”基于请求头重建 URL”这一类问题。多年来,许多 Web 框架都曾被同类问题波及(Django 的 ALLOWED_HOSTS、Rack、Express 等)。栈中任何从请求头重建 URL 之后又据此做信任决策的位置,都是审计的候选对象。

状态

项目出处日期备注
发现X41 D-Sec,OSTIF 资助的 vLLM 审计2026-01-27外部审计中发现
上游通报通知 Starlette 维护者2026-02-04附带可用 PoC
补丁提交提交 Pull request2026-03-01距离发布约 3 个月
修复版本Starlette 1.0.12026-05-21按 RFC 9112/3986 校验 Host
公开披露X41 D-Sec 公告 + badhost.org2026-05-22CVE-2026-48710 公布
受影响版本Starlette 0.8.3 → 1.0.0(含)所有基于其构建的 FastAPI 版本
受影响生态FastAPI、vLLM、LiteLLM、Text Generation Inference、FastMCP、MCP 服务器以及数千个智能体框架
扫描器 / 规则badhost.org、x41sec/poc GitHub 仓库2026-05-22Semgrep + CodeQL

这是一类需要反复学习的教训:当 AI 栈继承了某个框架层面的 bug,AI 本身并不会增加任何新的防御——它只会把暴露在互联网上的部署数量进一步放大。尽快打补丁,将中间件切换到 scope["path"],并停止把 Host 用于任何与访问控制相关的判断。

Sources