CVE-2026-48710:维护者的视角
摘要
Marcelo Trylesinski 分享了他对 CVE-2026-48710 的看法,这是一个 Starlette 中的安全漏洞,涉及通过操纵 Host 标头绕过基于路径的授权。他认为该漏洞源于应用模式和部署方式,而非框架本身。
<p><a href="https://lobste.rs/s/xvdvko/cve_2026_48710_maintainer_s_perspective">评论</a></p>
查看缓存全文
缓存时间:
2026/05/29 19:59
# CVE-2026-48710:来自维护者的视角 - Marcelo Trylesinski
来源:https://marcelotryle.com/blog/2026/05/28/cve-2026-48710-a-maintainers-perspective/
上周,我在 GitHub 上发布了安全公告 GHSA-86qp-5c8j-p5mr (https://github.com/Kludex/starlette/security/advisories/GHSA-86qp-5c8j-p5mr),现在该项目似乎正受到铺天盖地的负面报道。我想花点时间分享我对这件事的看法,以及对漏洞本身的立场,也聊聊整个过程。
## 漏洞是什么?
路由使用原始的 HTTP 路径,但 `request.url` 是通过拼接 `http://{host}{path}` 重建的,其中 `host` 来自 `Host` 头部。由于客户端可以控制该头部,因此 `request.url.path` 可能与实际路由所基于的路径不一致。
当中件件使用 `request.url.path` 来保护路由时,这就成了问题:
```python
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import PlainTextResponse
from starlette.routing import Route
class AuthMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
if request.url.path.startswith("/admin"):
return PlainTextResponse("Forbidden", status_code=403)
return await call_next(request)
async def potato(request):
return PlainTextResponse("Secret potato")
app = Starlette(
routes=[Route("/admin/potato", potato)],
middleware=[Middleware(AuthMiddleware)],
)
```
现在向 `/admin/potato` 发送一个请求,并将 `Host` 头部设为 `example.com/?`。由于路由使用原始路径,路由器仍然会分发到 `potato` 端点。但是 `request.url` 被重建为 `http://example.com/?/admin/potato`,因此 `request.url.path` 变成了 `/`。中间件的检查永远不会触发,机密信息就会泄露。
你可能会觉得:"哇,这真是个严重的 bug。"它确实可能很严重。但是,当你分析漏洞究竟在哪里时,假设条件非常重要:
1. 应用程序使用了基于路径的授权中间件。这是在 Starlette 之上构建的一种应用模式,并非 Starlette 本身提供的功能。
2. 路由本身从未被欺骗。路由器基于原始 HTTP 路径进行分发,因此运行的端点始终是正确的。只有那些从重建的 URL 中重新推导授权的代码才会受到影响。
3. 没有 CDN、负载均衡器、API 网关或前置 Web 服务器来验证 `Host` 头部。只要存在任何这些组件,就能通过拒绝格式错误的值来抵消攻击。
这里有一个更根本的观点,Giovanni Barillari (https://github.com/gi0baro) 在公告讨论中说得很好:授权和认证首先就不应该基于请求的路径、主机或查询字符串。无论有没有这个 bug,这都是一种脆弱的模式。尾随斜杠、大小写敏感性、百分号编码和路径规范化都会以同样的方式影响这类代码。`Host` 头部只是另一个能让匹配到的字符串与实际服务的请求产生差异的因素。
简而言之,这个漏洞源于应用程序模式和部署方式,**绝不是 Starlette 有意为之**。
## 那么为什么还要发布 CVE?
我认为 OSTIF 的文章 (https://ostif.org/disclosing-the-badhost-vulnerability-in-starlette/)对此解释得很好:
> 这个 bug 是一个典型的“责任差距”案例:如果这个维护者不进行修补,成千上万个暴露的项目将不得不单独保护它们自己。通过完成这项工作,他们自愿承担起了保护生态系统免受长期系统性损害的责任。与所有开源项目一样,他们本来什么都不欠我们,完全可以把这作为别人的问题不管,但他们却采取了非凡的措施来帮助生态系统。请考虑向 Kludex 捐赠:https://github.com/sponsors/Kludex
我觉得这总结得很好。
## 关于披露过程
我想公平地说:X41 D-Sec 的人总体上很有礼貌,在我提出反对时也道了歉,并且显然出于善意。但我对过程中的几点不太认同。
首先是最初的截止日期。第一条消息就规定了一个大约一个月的披露窗口,并提议“2 天或 5 天后”开个联合电话会议。这是把未付费的维护者当成有值机安全团队的企业来对待。参与内部讨论的人都有全职工作,只是在空闲时间维护这些项目。值得称赞的是,X41 在提出这一点后放宽了截止日期。
最让我难以接受的是,在接近尾声时,他们建议*在补丁可用之前*就发布公告。这是糟糕的做法。一个没有可用修复的公开公告让每个受影响的用户暴露无遗,只能干等,而攻击者却获得了完全相同的信息。协调披露的全部意义就在于先发布修复程序。对于一个部署如此广泛的包,建议反其道而行之是**与保护用户背道而驰的**。
另一件让我困扰的事情是,X41 建立了 badhost.org (https://badhost.org/),一个带有标志、名称和互联网范围扫描器的品牌化登陆页面。这是在进行漏洞营销。在公告发布的瞬间就上线一个专用网站,用户根本没有时间反应:CVE 数据库还没有传播,Dependabot 等工具还没有发出警报,大多数人甚至还没有机会升级依赖。品牌化吸引了注意力;而运行受影响软件的人们却只能从头条新闻中得知消息。
## Ars Technica 的不专业行为
Ars Technica 发表的文章 (https://arstechnica.com/information-technology/2026/05/millions-of-ai-agents-imperiled-by-critical-vulnerability-in-open-source-package/?comments-page=1#comments)中提到:
> Starlette 的开发人员没有立即回复寻求确认评估和更多信息的邮件。
以下是他们针对公开披露发送给我的邮件:
Ars Technica 记者 Dan Goodin 的邮件:“你好。我是 Ars Technica 的记者 Dan Goodin。我联系你是关于 CVE-2026-48710。它有多严重?你知道到目前为止有多少用户打了补丁吗?Starlette 每周有 3.25 亿次下载,对吗?即使很大一部分基础用户没有更新,这似乎也是一个潜在严重的问题。能否尽快提供答案和见解?此致。”
这封邮件是在文章发表前几小时发送的。我觉得我有权不回复这种粗鲁且咄咄逼人的语气。我之前从未与这个人互动过,我认为我不欠他们任何东西。
我不读 Hacker News 或 Reddit,除非有朋友指给我特定的帖子。而且……我的朋友们因为我不知道 Ars Technica 是什么而嘲笑我。但经历了今天之后,我很确定我再也不会读他们的任何东西了。
## 分类安全公告的负担
最后但并非最不重要的是,我想谈谈分类安全公告的负担。我没有安全团队,每个公告都落在我个人身上。大多数星期我几乎每天都在审查一个,而且其中绝大多数都是噪音——编码代理生成看似合理的报告,需要花实际时间才能驳斥。像这种真正的漏洞很少见,但它仍然需要与所有其他事情竞争同样的夜晚和周末。
我在 Starlette 1.0 文章 (https://marcelotryle.com/blog/2026/03/22/starlette-10-is-here/) 中写过:公告是维护工作的棘手部分,因为无视真正的问题很危险,而处理虚假的报告代价高昂。我会继续做下去,但值得理解在你最终看到的冷静回应背后所付出的成本。
## 你应该怎么做
升级到 Starlette 1.0.1 (https://www.starlette.io/release-notes/) 或更高版本,该版本会验证 `Host` 头部并拒绝格式错误的值。
除此之外:不要将授权基于 `request.url.path`。如果你需要路由路径,请使用 `request.scope["path"]`,它永远不会从 `Host` 头部重建。更好的是,根本不要根据路径字符串做授权决策。
## 感谢
最让我印象深刻的事情不是批评,而是有多少人在没有要求的情况下反对了这种框架。在 Ars 的讨论串中,一个接一个的人花时间解释,这是一个老式 bug,不是“AI 垃圾”,并且文章的角度对读者造成了伤害。攻击项目的评论很少,而且大多被踩了。
对于每一个读过头条并深入思考实际发生了什么的人:谢谢你们。❤
相似文章
Lobsters Hottest
Starlette 和 FastAPI 中存在一个严重的主机头身份验证绕过漏洞(CVE-2026-48710),影响众多 Python ASGI 应用,包括 AI 推理服务器(如 vLLM)、AI 代理服务器(如 LiteLLM)和 MCP 网关,可能导致未授权访问。
Ars Technica
开源ASGI框架Starlette中的一个严重漏洞(CVE-2026-48710,名为BadHost)使数百万AI代理和服务器面临数据被盗和凭证泄露的风险,影响了FastAPI、vLLM和LiteLLM等框架。该漏洞已在Starlette 1.0.1中修复,利用起来非常简单,凸显了AI工具生态系统中的风险。
Lobsters Hottest
在 Anthropic 的 Claude Code CLI 和 SDK 中发现了严重命令注入漏洞(CVE-2026-35022,CVSS 9.8),攻击者能够通过环境变量、文件路径和身份验证助手执行任意命令并窃取凭据。这些缺陷使得在 CI/CD 环境中能够进行毒化流水线执行攻击,需要立即修补和配置更改。
X AI KOLs Following
Next.js 存在一个严重漏洞(CVSS 8.6),影响版本 13.4.13+、14.x、15.x 以及 16.0.0–16.2.4,允许未经身份验证的攻击者访问内部服务、云凭证和 API 密钥。请立即升级到 15.5.16 或 16.2.5。
Lobsters Hottest
一份关于2026年5月发现的三个严重Linux本地权限提升漏洞的报告,强调了披露模型的崩溃及其对生产环境的影响。