一个 Host 头,理论上只是 HTTP 请求里的主机名。到了 BadHost 这里,它可能让应用“看错路”。

Starlette 1.0.1 之前版本被披露 CVE-2026-48710,别名 BadHost。X41 D-Sec 在 OSTIF 赞助审计中发现:Starlette 在构造 request.url 时,对 Host 头处理不够稳,特定构造下可能让 request.url.path 和真实请求路径不一致。

最要紧的一点:这不是“FastAPI 全线沦陷”。

最新披露材料把风险边界讲得更清楚了。进入主要风险区,要同时满足几件事:

  • 使用 Starlette 1.0.1 之前版本,或被 FastAPI 等依赖链带入旧版 Starlette;
  • 应用在中间件里用 `request.url.pathURL(scope=...).path` 做鉴权、限流、黑白名单;
  • 前面没有可靠校验 Host 头的反向代理;
  • 相关服务可被攻击者直接访问,或测试、灰度、自托管实例被暴露。

所以这件事的关键词不是“恐慌”,而是“代码模式”。

坏习惯被照亮了。

BadHost 到底发生了什么

BadHost 的核心很短:请求实际访问了受保护路径,但中间件可能以为它访问的是免鉴权路径。

大致链条是这样:

  • ASGI 服务器把请求头交给 Starlette;
  • Starlette 根据请求信息拼出 request.url
  • 异常 Host 头影响了 URL 构造;
  • 应用中间件读取 `request.url.path`;
  • 安全判断基于一个被污染的路径。

这类漏洞麻烦,不是因为某个业务接口忘了校验密码。它跨了几层:HTTP、ASGI、框架 URL 构造、业务中间件假设。

每一层都像在做分内事。组合起来,就给了攻击者缝隙。

可以把风险压成一张表:

场景风险判断原因
FastAPI 标准 Depends() / Security() 路由级鉴权不是主要风险点鉴权跟着路由和 endpoint 走,不靠 request.url.path 判断
自定义中间件用 request.url.path 放行路径高风险常见于内部后台、API 网关、模型服务保护
中间件改用 scope["path"] 判断真实路径风险显著下降ASGI path 来自请求行,不受 Host 头污染路径影响
反向代理严格校验 Host可中和这条攻击路径前提是确实拒绝或规范化非法 Host

一句话:如果你的权限边界写在字符串路径判断里,就该查。

AI 基础设施为什么被点名

BadHost 不是 LLM 漏洞。模型不会因为 Host 头突然变坏。

但 AI 基础设施确实更容易踩中这种坑。

过去两年,大量推理服务、Agent 后端、MCP 服务、LiteLLM/vLLM 这类网关或模型工具链,都用 FastAPI/Starlette 快速搭起来。服务多,接口杂,迭代急。很多团队会把“哪些路径免登录、哪些路径要 token”塞进中间件里。

这就是 BadHost 喜欢的土壤。

尤其是几类对象要认真排查:

  • 自托管模型网关和内部 API 网关;
  • 暴露在公网的 vLLM、LiteLLM、Agent 后端;
  • MCP/FastMCP 服务,尤其是带 OAuth 发现、健康检查、元数据端点的部署;
  • 临时映射出来的 Uvicorn、Hypercorn 服务;
  • 测试环境、灰度实例、容器端口直出。

MCP 场景更敏感。它本来就存在一些未鉴权发现端点。如果业务又用路径白名单做边界,攻击者更容易借一个“看起来免鉴权”的入口,去碰不该碰的接口。

这才是 AI 代理风险被 Host 头放大的原因。

不是模型更邪门,是工程边界更乱。

修复不难,难的是承认边界写错了

该做的动作很明确。

升级 Starlette 到 1.0.1 或更新版本。检查 FastAPI 依赖链里实际带入的 Starlette 版本,不要只看顶层包名。

然后查代码。

重点搜这些模式:

  • `request.url.path`
  • `URL(scope=...).path`
  • BaseHTTPMiddleware
  • if path in public_paths
  • startswith("/api/")
  • healthmetricsopenapioauth 相关白名单判断

更稳的做法,是把鉴权放回 endpoint 级别。

FastAPI 用 Depends() / Security(),Starlette 用 requires()。这些机制表达的是“这个接口需要什么权限”,不是让一个中间件凭字符串猜测“这条路该不该放行”。

如果必须在中间件里判断路径,优先用 scope["path"]。把 request.url.path 从安全决策里移出去。

运维侧也别只说“我们前面有 nginx”。要确认 nginx、Caddy、Traefik、HAProxy 是否真的校验并规范化 Host 头。

反向代理不是护身符。很多事故都不发生在正式域名入口,而发生在测试端口、临时容器、灰度节点、内部服务外露。

城门守得再严,后门敞着也没用。

我更在意的是这次暴露的工程习惯

BadHost 最有价值的地方,不是它多么玄妙。恰恰相反,它很土。

它像早年的 Host Header Injection,也像 X-Forwarded-* 信任链错误。都是同一种病:把客户端能影响的元信息,当成服务端可信事实。

这在 Web 世界早就演过。现在 AI 基础设施又演了一遍。

原因也不新鲜。AI 团队赶上线,先把模型跑起来,再补鉴权;先做 demo,再接网关;先开放内部接口,再想怎么收口。路径白名单看着省事,复制一段中间件就能跑。

省下来的时间,最后会在排查里还回去。

“天下熙熙,皆为利来。”放到这里不是说谁贪,而是说激励很直白:产品要快,集成要快,Agent 要快,工具调用要快。安全边界这种东西,看不到增长曲线,就容易被写成几行字符串判断。

BadHost 刺痛人的地方正在这里。它没有发明一种新风险,只是把旧风险照到了 AI 代理这堆新服务上。

模型越强,工具越多,后端越碎。碎到一定程度,安全问题就不再是某个接口的问题,而是组织有没有能力知道自己到底暴露了什么。

这也是我不太买账“升级一下就完事”的原因。

升级当然要做。但如果团队继续把鉴权放在中间件路径判断里,继续让测试服务直连公网,继续把 Host、Forwarded、Origin 这类可变输入当安全事实,下一个 BadHost 只是换个名字。

目前公开材料没有给出可核验的大规模利用案例,也没有可靠受害数量。别把它吹成灾难。

但也别轻描淡写。

接下来真正该看的不是全网扫出多少 Starlette,而是三个更实在的变量:

  • 主流 AI 服务框架和工具链是否快速升级 Starlette 依赖;
  • 企业代码库里 `request.url.path` 出现在安全中间件里的比例有多高;
  • 扫描工具能不能区分“用了相关 API”和“真的可达、可绕过”。

安全行业最容易犯的错,是把“版本命中”当成“风险命中”。工程团队最容易犯的错,是把“我们有代理”当成“边界可靠”。

BadHost 正好夹在这两个错之间。

它不大,却很典型。

一个 Host 头能闯出事,不是因为 Host 头权力太大,而是应用把权力给错了地方。