EN
en direct
DevOps Critique

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.

Un mur de casiers postaux gris identiques, une enveloppe à demi glissée dans le mauvais casier, son coin brillant d’un éclat ambre.

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.path est reconstruit par Starlette à partir du Host — 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 :

  1. 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.
  2. Auditez vos middlewares : recherchez toute lecture de request.url, request.url.path ou request.base_url utilisée dans une décision de sécurité, et remplacez-la par request.scope["path"].
  3. Validez le Host au niveau du frontal — reverse proxy ou TrustedHostMiddleware — pour rejeter les Host non autorisés avant qu’ils n’atteignent l’application.
  4. 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.

python
# ❌ 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)
python
# ✅ 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

cve

Vulnérabilités liées

Le brief cyber, chaque mardi

Les failles qui comptent, les correctifs à appliquer, en dix minutes de lecture.

Pas de spam. Désinscription en un clic.
à lire ensuite

Sur le même sujet

Docker Engine 29.8.0 ajoute l’option --umask et bloque un chemin d’évasion sandbox en 32 bits

Docker Engine 29.8.0, publié le 3 septembre 2026, introduit l’option --umask pour fixer le masque de permissions des processus d’un conteneur et ajoute des règles AppArmor/SELinux qui bloquent l’appel socketcall(2) 32 bits vers AF_VSOCK. Mettez à jour si vous partagez des volumes entre hôte et conteneur ou si vous durcissez vos conteneurs.

Kubernetes 1.37 passe le mode rootless en bêta pour les composants du nœud

La version 1.37 de Kubernetes active par défaut KubeletInUserNamespace : kubelet, runtimes et CNI peuvent désormais tourner en tant que non-root dans un user namespace. Activez-le sur vos nœuds pour confiner les failles de type container-breakout loin des privilèges root de l’hôte.

← Retour au fil

Tapez au moins deux caractères.

naviguer ouvrir esc fermer