BadHost contourne l’authentification de FastAPI avec un seul caractère dans l’en-tête Host
CVE-2026-48710, surnommée BadHost, permet à un attaquant non authentifié de transformer une requête bloquée en requête autorisée en ajoutant un caractère à l’en-tête Host, dans toutes les versions de Starlette antérieures à 1.0.1. Mettez à jour Starlette et auditez chaque middleware qui lit request.url.path au lieu du chemin ASGI brut.
2 septembre 2026. CISA ajoute CVE-2026-48710 au catalogue des vulnérabilités exploitées connues (KEV), avec un score EPSS au 98e centile. Surnommée BadHost, la faille touche Starlette, le framework ASGI qui porte FastAPI et une part considérable de l’écosystème Python — dont vLLM, LiteLLM et de nombreux serveurs MCP. Le principe tient en une phrase : un en-tête Host forgé permet de contourner les middlewares d’authentification qui lisent request.url.path. Pourquoi c’est important : plus de 400 000 projets dépendent de Starlette, et le correctif change une version majeure.
Un en-tête Host que Starlette ne validait pas
La cause racine est un défaut de validation de l’en-tête Host. Dans les versions de Starlette comprises entre 0.8.3 et 1.0.0 (le correctif arrive en 1.0.1), le framework reconstruit request.url à partir du Host envoyé par le client sans vérifier sa cohérence avec la requête réelle.
La conséquence est un désaccord de parsing — le même mécanisme que le request smuggling classique, appliqué à un seul header. Le serveur ASGI sous-jacent (uvicorn, hypercorn) traite la requête avec un chemin, tandis que l’application Starlette en calcule un autre à partir du Host falsifié. Un attaquant peut donc injecter un chemin dans la valeur du Host, et faire croire à l’application que la requête vise un endpoint autorisé.
Le nom BadHost vient de là : la faille ne casse pas le serveur, elle fait mentir l’application sur sa propre URL.
Le vrai bug est dans votre middleware, pas dans Starlette
Starlette fournit le mécanisme. L’exploitation, elle, se joue dans le code applicatif — précisément dans les middlewares et décorateurs qui prennent des décisions de sécurité à partir de request.url.path au lieu du chemin ASGI brut (scope["path"]).
La différence est cruciale :
scope["path"]est le chemin réellement reçu par le serveur ASGI, celui que l’attaquant ne peut pas falsifier une fois la requête acceptée.request.url.pathest reconstruit par Starlette à partir duHost— et donc falsifiable.
Un middleware d’authentification qui fait confiance à request.url.path croit contrôler un chemin sûr alors que la requête réelle en vise un autre. Ajoutez un caractère au Host, et une requête destinée à une route protégée est réécrite en route autorisée. La porte n’est pas forcée : le gardien regarde simplement la mauvaise adresse.
FastAPI est touché de plein fouet, car ses dépendances et ses middlewares d’autorisation sont construits au-dessus de cette couche. vLLM, LiteLLM et les serveurs MCP héritent du même défaut, ce qui étend le rayon d’action aux infrastructures d’agents IA exposées en production.
Une sévérité sous-estimée par le mainteneur
Le débat sur la sévérité est lui-même un enseignement. Le mainteneur de Starlette a évalué la faille comme Modérée — argument : la validation du Host relève en partie de la configuration du serveur ou du proxy frontal. Les chercheurs qui ont documenté BadHost répondent que la sévérité réelle dépend du code qui consomme request.url, et qu’une faille qui contourne silencieusement l’authentification de centaines de milliers de déploiements ne peut pas être « modérée ».
CISA a tranché en ajoutant la faille au KEV le 2 septembre 2026, avec la mention d’une exploitation active. Le 98e centile EPSS — c’est-à-dire une probabilité d’exploitation à 30 jours supérieure à la quasi-totalité des CVE — confirme que ce n’est pas un exercice théorique.
La leçon pour les équipes est double. Premièrement, un avis de sévérité éditeur est un point de départ, pas un verdict : c’est votre code qui détermine l’impact réel. Deuxièmement, toute dépendance qui reconstruit une URL à partir d’un header non validé est une bombe à retardement dans un middleware de sécurité.
Comment corriger et vérifier
Le correctif est clair, mais l’audit est la partie qui prend du temps. Voici l’ordre des opérations :
- Mettez à jour Starlette en 1.0.1 (ou version supérieure) dans toutes vos dépendances, y compris transitives — FastAPI, vLLM, LiteLLM et les serveurs MCP qui l’embarquent.
- Auditez vos middlewares : recherchez toute lecture de
request.url,request.url.pathourequest.base_urlutilisée dans une décision de sécurité, et remplacez-la parrequest.scope["path"]. - Validez le
Hostau niveau du frontal — reverse proxy ouTrustedHostMiddleware— pour rejeter lesHostnon autorisés avant qu’ils n’atteignent l’application. - Vérifiez le build de dépendances avec un scan qui signale Starlette < 1.0.1, y compris dans les images de conteneurs et les lambdas.
Le principe de fond tient en une règle simple à retenir : les décisions de sécurité se prennent sur le chemin brut du serveur, jamais sur une valeur reconstruite à partir d’un header contrôlé par le client.
Le motif vulnérable et son correctif
Pour fixer les idées, voici le motif à proscrire et le motif sûr. Le premier lit le chemin depuis request.url, reconstruit à partir du Host ; le second lit le chemin brut fourni par le serveur ASGI.
# ❌ Vulnérable — request.url.path est reconstruit depuis le Host (falsifiable)
async def auth_middleware(request, call_next):
if request.url.path.startswith("/admin"):
# décision de sécurité fondée sur une valeur contrôlée par le client
return Response(status_code=401)
return await call_next(request) # ✅ Correct — scope["path"] est le chemin réellement reçu par le serveur
async def auth_middleware(request, call_next):
if request.scope["path"].startswith("/admin"):
return Response(status_code=401)
return await call_next(request) La règle se lit en une phrase : toute décision de sécurité qui touche au chemin, à l’hôte ou au schéma doit lire request.scope, jamais request.url.
Cette subtilité explique aussi pourquoi la faille est passée inaperçue. Le code vulnérable fonctionne en conditions normales : tant que le Host envoyé est honnête, request.url.path et scope["path"] coïncident. Le défaut ne se manifeste que lorsqu’un attaquant falsifie le Host — un cas que les tests unitaires ne couvrent presque jamais. C’est le type de bug que seule une revue orientée sécurité du middleware détecte, et c’est précisément ce que l’audit post-correctif doit viser.
Verdict
Si vous déployez FastAPI, vLLM, LiteLLM ou un serveur MCP, mettez à jour Starlette en 1.0.1 immédiatement et auditez chaque middleware d’authentification qui lit request.url.path — c’est là que l’exploitation se joue, pas dans le framework seul.
Si vous opérez un reverse proxy devant ces services, ajoutez dès maintenant une validation stricte du Host : c’est la mesure qui neutralise l’attaque même si une dépendance interne reste en retard.
Si vous gérez un SI qui consomme des dépendances Python en transit, intégrez la règle « Starlette ≥ 1.0.1 » à votre politique de build, car la faille peut arriver par une bibliothèque que vous ne voyez pas directement.
Références
- CISA — Known Exploited Vulnerabilities, CVE-2026-48710, 2 septembre 2026
- Corgea — CVE-2026-48710: Starlette lets a forged Host header lie about request.url
- Ionix — BadHost, Host header authentication bypass in Starlette
- SecurityArsenal — Starlette HTTP request/response smuggling in CISA KEV
- NVD — CVE-2026-48710