A single malformed HTTP header is enough to walk past the auth middleware of thousands of Python AI services. That is BadHost — CVE-2026-48710 — an authentication bypass in Starlette, the ASGI framework underneath FastAPI, vLLM, LiteLLM, Ray Serve, BentoML, Google’s ADK-Python, and the long tail of custom agent backends built on them. X41 D-Sec found it during an OSTIF-sponsored audit and published coordinated advisories on May 22, 2026, with additional reporting credited to ehhthing and Nicolas Lamoureux. Starlette alone counts more than 400,000 dependent repositories on GitHub. The fix is in Starlette 1.0.1.
The Bug
Starlette reconstructs the full request URL by stitching together the scheme, the Host header, and the request path, then re-parsing the result. Versions before 1.0.1 never validated the Host value before doing that. Inject a /, ?, or # into the header and you shift the path, query, and fragment boundaries during the re-parse — so request.url.path returns something different from the route the ASGI server actually dispatched to.
Concretely, an attacker sends:
| |
Starlette rebuilds the URL as http://example.com/health?x=/admin. The router still delivers the request to /admin, because routing uses the real HTTP request line — but request.url.path, the value middleware reads to decide who gets in, now returns /health. Auth middleware that checks “is this path on my public allowlist?” sees /health, decides it is a harmless public endpoint, and waves the request through to /admin with no credentials. The trick works against both allowlist (“let /health through”) and denylist (“block everything except /health”) patterns.
Delivering it requires a raw TCP socket — standard HTTP clients normalize the Host header before it ever leaves the machine, which is also why the bug hid in plain sight for so long. No single layer is wrong on its own: the ASGI server forwards the raw header, Starlette trusts it to rebuild the URL, and middleware authors assume request.url.path is canonical. The vulnerability only exists at the seam between them, which is exactly why scanners missed it.
Why AI Infrastructure Is the Bullseye
This is acutely bad for AI deployments. The Model Context Protocol mandates unauthenticated OAuth discovery endpoints at well-known public paths — and a known public path is precisely the skeleton key BadHost needs. Inject one into the Host header and a Starlette-based MCP server’s protected tools, internal endpoints, and stored credentials become reachable without authenticating. MCP servers hold credentials for every system they broker, so the blast radius is the whole connected estate, not one app.
vLLM, LiteLLM, Ray Serve, BentoML, ADK-Python, and any custom FastAPI agent backend using BaseHTTPMiddleware for access control are in scope. Consequences range from free model access and leaked provider API keys to, in some cases X41 confirmed via CodeQL, remote code execution. One important exception: FastAPI’s Depends() and Security() dependencies are not affected — they enforce auth at the route level, not against a path string. The danger is specifically custom middleware that trusts request.url.path.
Are You Affected
You are exposed if a Starlette/FastAPI app (pre-1.0.1) makes authorization decisions in BaseHTTPMiddleware based on request.url.path, and the app is reachable without an RFC-compliant reverse proxy in front. An nginx, Caddy, Traefik, or HAProxy front end that rejects malformed Host headers blunts the attack before it lands — but self-hosted inference servers, dev, and staging environments frequently expose the ASGI server directly.
Mitigation
Upgrade Starlette to 1.0.1 or later now; the patch validates Host against the grammar in RFC 9112/3986 and falls back to scope["server"] for malformed values. Then, regardless of version, switch any path-based middleware to read scope["path"] instead of request.url.path — the ASGI scope path comes from the HTTP request line and cannot be poisoned via the header. Put an RFC-compliant reverse proxy in front of every ASGI server, and prefer endpoint-level auth (requires(), Depends(), Security()) over middleware that inspects path strings. To find exposure, run the free scanner at badhost.org or the open-source Semgrep rules and CodeQL queries from X41’s repository against your own code.
The PoC is public and the attack is trivially automatable. The AI API keys sitting behind that middleware are worth far more than a point-release upgrade.
Advisory: X41-2026-002 — Starlette.