Tout le monde panique à cause de l'attaque LiteLLM. J'ai audité mes serveurs MCP et trouvé 7 failles bien pires.

8 min read

L'attaque de la chaîne d'approvisionnement LiteLLM la semaine dernière a été un réveil brutal. Auditez vos dépendances. Vérifiez ce que vous installez. Arrêtez de faire confiance aveuglément. Les hackers manient désormais des armes de précision à tête chercheuse et vous courez partout avec un bouclier en carton.

Après l'audit de mon projet principal, j'ai ouvert mes serveurs MCP secondaires. Ceux que j'ai construits moi-même, un mardi quelconque, parce que j'en avais besoin et que je ne voulais pas réfléchir à l'auth à ce moment-là. J'ai trouvé 7 vulnérabilités critiques. Toutes conformes aux spécifications. Aucune n'aurait déclenché d'alerte, de lint, d'audit. Parce que le protocole dit que c'est correct.

LiteLLM, c'était quelqu'un qui empoisonnait le code de quelqu'un d'autre. Mon setup MCP était une attaque de chaîne d'approvisionnement que je me suis infligée à moi-même. Et si vous faites tourner MCP en production, il y a de bonnes chances que vous ayez fait la même chose.

TLDR : J'ai audité mes propres serveurs MCP après l'incident LiteLLM. 7 failles critiques, toutes conformes aux specs. Le problème n'est pas la négligence des développeurs, c'est un protocole qui a rendu le chemin non sécurisé plus facile que le sécurisé. Un après-midi de corrections, 0€ d'infrastructure supplémentaire, et j'ai probablement évité que mes données de production se barrent par la porte. Allez ouvrir votre mcp.json.

Illustration humoristique comparant un développeur paniqué avec des tokens en texte brut exposés versus un développeur confiant avec des configs chiffrées et une authentification appropriée
Vos configs MCP sont un désastre sécuritaire. En voici la preuve.

L'erreur d'auditer mon propre setup MCP

La semaine dernière, j'ai écrit sur l'attaque de chaîne d'approvisionnement LiteLLM et 8 mois d'installations pip non supervisées. Tout le monde a hoché la tête. "Oui, on devrait auditer nos dépendances." "Oui, les attaques de chaîne d'approvisionnement font peur." "Oui, quelqu'un devrait faire quelque chose." Le consensus, c'est confortable comme ça.

Puis j'ai fait l'erreur de suivre mes propres conseils.

Pas sur les dépendances cette fois. Sur mes propres serveurs MCP. Ceux que j'ai écrits, déployés, et connectés à Claude Code parce que j'en avais besoin et je me suis dit : mon code, mon infra, mes outils. Ça peut être si grave que ça ?

Sept vulnérabilités critiques de grave.

MCP a un problème de confiance par design

Je n'ai pas livré ces 7 vulnérabilités parce que je suis négligent. Je les ai livrées parce que les specs de MCP en ont fait la chose rationnelle à faire un mardi après-midi quand mes gosses hurlaient pour aller à la piscine.

Le protocole a été conçu pour une chose : faire communiquer les outils entre eux le plus vite possible. La sécurité a eu droit au traitement "détail d'implémentation", ce qui en design de protocole signifie "personne ne le fera."

Pas d'identité native. Un token est un token. Votre Claude Desktop et un script écrit par un inconnu ont la même tête pour un serveur MCP. Le protocole n'a aucun mécanisme pour les distinguer, donc il n'essaie pas.

Pas d'application du moindre privilège. Vous connectez un outil, il obtient tout. Le scoping granulaire ? Construisez-le vous-même. Les specs n'esquissent même pas à quoi devraient ressembler les scopes. Donc vous ferez comme moi : l'ignorer.

Pas de piste d'audit. Quelque chose foire, vous grep-ez les logs nginx à 23h en espérant trouver quelque chose d'utile. Le protocole ne définit pas ce que "logger un appel d'outil" signifie, donc la plupart des serveurs ne le font pas.

La communauté dev sait que c'est cassé. Le débat "RIP MCP" a été bruyant sur X, et pour de bonnes raisons. La frustration ne porte pas sur les bugs. Elle porte sur un modèle de confiance qui n'a jamais existé pour commencer.

Les specs s'améliorent. La v2025-06-18 a ajouté des éléments d'auth. Mais des milliers de serveurs en production ont été construits sur les défauts permissifs, et les specs n'imposent pas de remédiation rétroactive. Les specs se sont améliorées. La base installée, non.

Découvertes qui sont la faute des specs (et la mienne)

Je vais assumer chacune avant de l'expliquer. Ce que j'ai fait d'abord, puis pourquoi les specs me l'ont permis.

Découverte 1 : Le token JWT de 100 ans

const token = jwt.sign(
  { serverId: 'content-api' },
  SECRET,
  { expiresIn: '100y' }
);

Je ne voulais pas gérer la logique de refresh de token. Donc j'ai écrit expiresIn: '100y' et j'ai continué ma vie. Ce n'est pas un token. C'est un mot de passe permanent déguisé en JWT. Les specs MCP ne mentionnent pas les durées de vie maximales des tokens. L'expiration des tokens est "un détail d'implémentation." Donc j'ai implémenté : jamais.

Découverte 2 : Enregistrement OAuth ouvert

J'étais le seul utilisateur. Pourquoi construire une whitelist pour une fête d'une personne ?

Les specs n'exigent pas de liste de clients pré-approuvés. Donc n'importe quel script pouvait s'enregistrer comme client OAuth et obtenir un accès complet. L'enregistrement est resté ouvert parce que le fermer semblait excessif, et l'excessif un mardi après-midi entre deux déploiements, ça n'arrive pas.

Découverte 3 : Zéro scoping

Un token, tous les outils. Lecture, écriture, admin. La même clé ouvre toutes les portes. MCP ne définit pas de modèle de permissions, donc je n'en ai pas construit. Mon contrôle d'accès avait exactement un niveau : tout.

Découverte 4 : Pas de limitation de débit

Pas de throttle, pas de circuit breaker. Un token compromis pouvait exfiltrer tout le dataset en balançant des requêtes à pleine vitesse. Personne ne compte parce que le protocole ne dit pas que quelqu'un devrait compter.

Les trois restantes (tokens bearer stockés en clair dans ~/.claude/mcp.json, le même token copié-collé dans 3 fichiers de config, un PAT GitHub qui traîne dans settings.json) sont documentées dans l'article où j'ai trouvé mes propres secrets éparpillés dans les fichiers de config après un simple déplacement de dossier. Version courte : identifiants en clair dans votre répertoire home, dupliqués partout, pas de chiffrement.

Sept découvertes. Toutes conformes aux specs. Voici comment elles s'enchaînent.

La chaîne d'attaque dont personne ne parle

Ce qui me dérange, ce ne sont pas les découvertes individuelles. C'est la cascade.

Étape 1. Accéder au fichier de config. ~/.claude/mcp.json. Répertoire home. Pas caché, pas chiffré, pas protégé par quoi que ce soit à quoi vous avez probablement pensé.

Étape 2. Lire le token. Il est en clair. C'est tout. C'est l'exploit.

Étape 3. Appeler les endpoints MCP. Lister tous les outils, lire tout le contenu, accéder à toutes les ressources. Pas de restrictions de scope parce qu'il n'y en a pas.

Étape 4. Frapper l'API HTTP. Les mêmes tokens marchent parce que vous ne maintiendriez pas deux systèmes d'auth séparés pour les mêmes données (admettez-le, vous non plus).

Étape 5. La production est compromise. Contenu, données, configuration. Tout.

Temps total écoulé : quelques minutes.

Voici ce qui devrait vous déranger plus que LiteLLM : rien de tout ça ne nécessite d'attaque de chaîne d'approvisionnement. Personne n'a besoin de compromettre un package sur PyPI. Personne n'a besoin d'injecter du code malveillant. Toute la surface d'attaque est un fichier de config dans votre répertoire home que vous n'avez pas ouvert depuis le jour où vous l'avez généré.

Diagramme de chaîne d'attaque. Flux linéaire avec 5 étapes : fichier de config (~/.claude/mcp.json) → extraction de token (lecture en clair) → accès aux endpoints MCP (lister les outils, lire le contenu) → accès à l'API HTTP (mêmes tokens) → compromission de production (exfiltration de données). Chaque étape montre ce qui est exposé.
Chaîne d'attaque des tokens MCP

Le test du miroir

Pourquoi j'ai fait tout ça ?

Je suis assis dans mon appartement à Panama, la clim gagne à peine contre l'humidité, et je fixe un token JWT auquel j'ai donné une durée de vie de 100 ans. J'ai écrit 4 articles sur la sécurité en 8 jours. Je connais ce truc. Et j'ai quand même fait le truc facile à chaque fois parce que le protocole me le permettait.

Deedy Das a eu l'analyse la plus honnête que j'aie lue là-dessus : la prémisse fondamentale du vibecoding est la prémisse d'une attaque de chaîne d'approvisionnement. Vous exécutez du code que vous n'avez pas lu, de sources que vous n'avez pas vérifiées, avec des permissions que vous n'avez pas scopées. Ce n'est pas un workflow, c'est un modèle de menace.

Les chiffres confirment que ce n'est pas juste moi qui suis idiot. Les chercheurs en sécurité ont trouvé environ 7 000 serveurs MCP exposés sur l'internet ouvert. Environ la moitié n'avaient aucun contrôle d'autorisation. CVE-2025-6514 a atteint CVSS 9.6 pour injection de commande par manipulation d'entrée basique. Ce ne sont pas des cas limites. Ce sont les défauts.

Et c'est là que je pense que tout l'argument "les développeurs doivent être plus prudents" s'effondre. J'étais prudent. J'avais littéralement passé la semaine précédente à écrire sur les attaques de chaîne d'approvisionnement. Ça n'a pas eu d'importance. Le chemin de moindre résistance du protocole m'a mené droit dans chaque trou, parce que concevoir un setup sécurisé signifiait construire tout ce que les specs avaient omis, et construire tout ce que les specs avaient omis signifiait ne pas livrer cet après-midi-là.

Il y a une raison pour laquelle les CLI n'ont pas ce problème quand vous les utilisez comme outillage d'agent IA. Pas de couche d'abstraction qui cache la surface d'attaque. Vous voyez ce qui s'exécute. Vous contrôlez ce qui est exposé. Le modèle de sécurité est là dans la commande, pas enterré quelque part dans des specs que personne ne lit.

La correction d'un après-midi

Un après-midi. Voici ce qui a changé, et je suis presque agacé du peu de travail que ça a représenté.

La rotation des tokens était la première chose. Chaque service a maintenant son propre token. API de contenu, analytics, déploiement. Tokens séparés, révocation séparée. J'aurais dû faire ça dès le premier jour, mais "un token pour tout" semblait efficace à l'époque où je pensais qu'efficace et sécurisé étaient la même chose (ce n'est pas le cas, et je le savais, et je l'ai fait quand même).

Les tokens de courte durée ont pris 15 lignes de code. J'ai emprunté le pattern de comment Convex gère l'auth : générer, utiliser, expirer en 5 minutes. J'ai passé plus de temps à trop réfléchir à l'approche qu'à vraiment écrire le refresh de token. L'ancien token avait une durée de vie de 100 ans. Le nouveau vit moins longtemps que mon café du matin reste chaud.

Le scoping était la partie que je redoutais sans raison. Trois niveaux : lecture seule pour les dashboards, lecture-écriture pour les opérations de contenu, admin pour le déploiement. Chaque token sait ce qu'il peut toucher. Ça a pris peut-être 40 minutes en incluant les tests.

Limitation de débit : 10 requêtes par minute pour les écritures, 60 pour les lectures. Un token compromis a maintenant une limite de vitesse.

J'ai tué l'enregistrement ouvert. Liste de clients pré-approuvés seulement. Pas sur la liste, pas de token.

Total : 0€ d'infrastructure supplémentaire. Mêmes serveurs, même setup. La correction n'a jamais été chère. Le coût était d'assumer que "mon code, mon infra" signifiait "sûr par défaut."

En regardant en arrière, le token de 100 ans n'aurait jamais existé si j'avais appliqué les contrats de prompt dès le départ. Quand vous spécifiez vos contraintes de sécurité avant d'écrire le code, "je gérerai l'auth plus tard" cesse d'être une option valide. Les specs l'attrapent avant que le code ne soit livré.

Les défauts battent les intentions à chaque fois.

Votre fichier de config est juste là

Votre mcp.json traîne dans votre répertoire home en ce moment même. Quand l'avez-vous ouvert pour la dernière fois ?

Maintenant vérifiez les autres.

Sources

SC Media : analyse de sécurité MCP (serveurs exposés, CVE-2025-6514)

(*) La couverture est générée par IA. Les tokens dans l'image sont plus sécurisés que ceux que j'ai trouvés dans mes fichiers de config.