BadHost (CVE-2026-48710) : un caractère dans l'en-tête Host suffit à contourner l'auth dans Starlette, vLLM et FastMCP
X41 D-Sec a divulgué le 22 mai 2026 un contournement d'authentification critique dans Starlette < 1.0.1. Un seul / ? ou # dans l'en-tête HTTP Host désynchronise le chemin routé du chemin vu par le middleware, cassant l'autorisation par chemin dans vLLM, LiteLLM, FastMCP et des milliers d'agents IA construits sur FastAPI.
De quoi parle-t-on ?
Le 22 mai 2026, la société allemande de sécurité X41 D-Sec a publiquement divulgué la CVE-2026-48710, baptisée BadHost : un contournement d’autorisation non authentifié dans le framework ASGI Python Starlette, qui constitue le cœur de routage de FastAPI. La vulnérabilité a été découverte le 27 janvier 2026 lors d’un audit du serveur d’inférence vLLM financé par l’OSTIF, signalée aux mainteneurs de Starlette le 4 février, puis corrigée le 21 mai 2026 dans la version 1.0.1 de Starlette — soit un jour avant la divulgation publique.
Starlette est téléchargé environ 325 millions de fois par semaine. À travers FastAPI, il sous-tend une part très significative de la pile d’infrastructure IA moderne : vLLM, LiteLLM, Text Generation Inference, les proxys d’API compatibles OpenAI, les serveurs MCP (dont FastMCP), les plans de contrôle d’agents et les tableaux de bord de model serving. Toutes les versions de 0.8.3 à 1.0.0 incluses sont concernées.
Le bug est minuscule, le rayon d’impact est immense, et le délai entre le correctif et la divulgation a été pratiquement nul.
Comment ça marche
Starlette reconstruit request.url en concaténant l’en-tête HTTP Host avec le chemin de la requête puis en re-parsant le résultat comme une URL. La valeur de Host n’est pas validée contre la grammaire RFC 9112 / RFC 3986 avant la reconstruction. Tout octet envoyé par le client dans Host: est injecté directement dans le parser d’URL.
Si un attaquant insère /, ? ou # dans l’en-tête Host, les frontières entre autorité, chemin, query et fragment se décalent au re-parse. Le chemin que voit le routeur (issu du scope ASGI, dérivé de la ligne de requête HTTP) et le chemin que voit le middleware (request.url.path) divergent. Toute décision d’autorisation prise dans le middleware sur la base du chemin s’applique alors à une chaîne différente de celle sur laquelle le routeur dispatche.
# Schéma conceptuel de la désynchronisation, à partir de la
# divulgation publique du 22 mai 2026. Aucun payload visant
# un système en production n'est reproduit ici.
Filaire : GET /admin HTTP/1.1
Host: foo?
Scope ASGI (vérité) :
scope["path"] = "/admin" # le routeur dispatch ici
Reconstruction Starlette :
request.url = "http://foo?/admin"
request.url.path = "/" # le middleware voit ceci
Middleware :
if request.url.path.startswith("/admin"): # False
require_auth()
# auth contournée
Routeur :
dispatch("/admin") # le handler s'exécute
L’effet net : une requête qui renverrait normalement 403 Forbidden sur une route protégée renvoie 200 OK dès qu’un seul caractère illégitime est ajouté à l’en-tête Host, pendant que le handler protégé s’exécute quand même.
Le schéma est générique. Que le middleware soit une bibliothèque tierce ou un BaseHTTPMiddleware artisanal, ce qui compte est de savoir s’il lit request.url.path (empoisonné) ou scope["path"] (la vérité) pour prendre sa décision de sécurité.
Pourquoi ça compte
Trois propriétés rendent BadHost particulièrement préoccupant dans un contexte de sécurité IA.
D’abord, l’effet cascade. Starlette n’est pas une dépendance de niche. FastAPI repose dessus, et FastAPI est le framework de facto pour expédier en Python des serveurs de modèles et des plans de contrôle d’agents en 2025-2026. X41 et OSTIF citent explicitement vLLM, LiteLLM, les proxys compatibles OpenAI et les serveurs MCP parmi les projets touchés. Quiconque exploite un endpoint d’inférence auto-hébergé, une gateway LLM interne ou un serveur FastMCP derrière une ACL fondée sur des chemins doit présumer la vulnérabilité jusqu’à preuve du patch et de l’audit.
Ensuite, les serveurs MCP sont particulièrement exposés. La spécification du Model Context Protocol impose des endpoints de découverte OAuth non authentifiés ; ces routes sont accessibles sans identifiant et cohabitent avec des routes administratives qui, elles, sont censées être authentifiées. Cette disposition est un terrain idéal pour un contournement d’autorisation qui pivote d’une route publique vers une route protégée via la confusion du middleware.
Enfin, le calendrier de divulgation. La vulnérabilité a été signalée à l’amont début février. Un patch a été proposé début mars. Le correctif n’a été publié que le 21 mai, et X41 a diffusé tous les détails dès le lendemain — le délai pratique pour les opérateurs s’est compté en heures, pas en jours ni en semaines comme c’est l’usage. Pour des bibliothèques aussi largement embarquées, c’est exceptionnellement court, et les défenseurs doivent partir du principe que le scan et l’exploitation opportuniste sont déjà en cours.
Défenses
Le chemin de remédiation est court et concret.
Mettre à jour Starlette en 1.0.1 ou supérieur. C’est la mesure minimale et elle résout la cause racine du problème de parsing. FastAPI, vLLM, LiteLLM, FastMCP et la plupart des frameworks d’agents dépendent transitivement de Starlette ; il faut épingler la dépendance et reconstruire les images. Le patch valide l’en-tête Host contre la grammaire RFC 9112 / 3986 et bascule sur scope["server"] en cas de valeur malformée.
Déplacer les décisions de sécurité de request.url.path vers scope["path"] dans tout middleware sur mesure. Le chemin du scope ASGI provient de la ligne de requête HTTP et ne peut pas être empoisonné par une injection dans Host. C’est le correctif architecturalement juste, qui devrait survivre à n’importe quelle CVE individuelle : ne jamais accorder de confiance à une valeur dérivée d’un en-tête contrôlé par le client pour un choix d’autorisation.
Auditer immédiatement vos propres middlewares, y compris toute sous-classe de BaseHTTPMiddleware et tout middleware ASGI brut. X41 met à disposition un scanner distant gratuit sur badhost.org, ainsi que des règles Semgrep et des requêtes CodeQL publiées sur GitHub pour la détection statique des schémas vulnérables. À passer sur chaque service FastAPI interne, serveur de modèles et hôte MCP.
Ajouter un garde-fou en reverse-proxy. Configurer NGINX, Envoy, Traefik ou l’ingress pour rejeter toute requête dont l’en-tête Host contient /, ?, #, un espace ou tout octet hors de la grammaire d’autorité RFC 9112. C’est une ceinture de sécurité utile contre la prochaine variante de cette classe de bugs.
Traiter les serveurs MCP / FastMCP comme une surface d’attaque de production. Si votre hôte MCP cohabite avec des endpoints d’administration, isolez-les dans un plan réseau séparé, exigez de l’authentification par certificat client ou mTLS sur les routes administratives, et cessez de vous reposer sur les seuls préfixes de chemin pour l’autorisation. Les endpoints de découverte non authentifiés imposés par la spec MCP rendent les ACL par chemin fragiles par construction.
Re-tester la classe entière. BadHost est un bug de reconstruction d’URL à partir d’en-têtes. La même famille de problèmes a frappé de nombreux frameworks web au fil des ans (Django ALLOWED_HOSTS, Rack, Express). Toute portion de votre pile qui reconstruit une URL depuis des en-têtes de requête puis y fonde une décision de confiance est candidate à un audit.
Statut
| Élément | Référence | Date | Notes |
|---|---|---|---|
| Découverte | X41 D-Sec, audit vLLM financé par OSTIF | 2026-01-27 | Trouvé lors d’un audit externe |
| Signalement amont | Mainteneurs Starlette notifiés | 2026-02-04 | Avec PoC fonctionnel |
| Patch proposé | Pull request ouverte | 2026-03-01 | ~3 mois jusqu’à la sortie |
| Version corrigée | Starlette 1.0.1 | 2026-05-21 | Validation Host contre RFC 9112/3986 |
| Divulgation publique | Avis X41 D-Sec + badhost.org | 2026-05-22 | CVE-2026-48710 publiée |
| Versions affectées | Starlette 0.8.3 → 1.0.0 incluses | — | Toutes les builds FastAPI dessus |
| Écosystème touché | FastAPI, vLLM, LiteLLM, Text Generation Inference, FastMCP, serveurs MCP | — | Plus des milliers de frameworks d’agents |
| Scanner / règles | badhost.org, dépôt GitHub x41sec/poc | 2026-05-22 | Semgrep + CodeQL |
La leçon est de celles qu’on réapprend régulièrement : quand une pile IA hérite d’un bug de framework, l’IA n’apporte pas de défense supplémentaire — elle se contente de multiplier les déploiements exposés sur Internet. Patcher vite, basculer le middleware sur scope["path"], et cesser de faire confiance à Host pour quoi que ce soit qui contrôle un accès.
Sources
- → https://badhost.org/
- → https://ostif.org/disclosing-the-badhost-vulnerability-in-starlette/
- → https://news.ycombinator.com/item?id=48277107
- → https://www.cyberkendra.com/2026/05/badhost-cve-2026-48710-one-rogue-header.html
- → https://mlq.ai/news/critical-badhost-flaw-in-starlette-exposes-millions-of-ai-agent-deployments-to-auth-bypass/
- → https://gigazine.net/gsc_news/en/20260527-millions-ai-agents-imperiled-vulnerability-starlette/