一个 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.path
或URL(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`
BaseHTTPMiddlewareif path in public_pathsstartswith("/api/")health、metrics、openapi、oauth相关白名单判断
更稳的做法,是把鉴权放回 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 头权力太大,而是应用把权力给错了地方。
