vLLM et trust-remote-code : verrouillage de version, clé API et examen du code n’ont pas arrêté une seule requête
Si vous exécutez vLLM sur votre propre serveur et servez un modèle multimodal avec --trust-remote-code, vous comptez probablement sur trois protections. Vous avez examiné le code du processor avant d’activer l’option. Vous avez verrouillé la version du modèle (revision) pour que le code ne change pas à votre insu. Vous avez défini --api-key pour que seules les personnes autorisées puissent envoyer des requêtes. Un nouvel avis de sécurité du projet vLLM décrit une situation où, dans une configuration particulière avec --trust-remote-code, une seule requête vers /tokenize peut choisir le code Python qui sera exécuté sur le serveur. Cette route n’est pas protégée par la clé API.
Notre constat est que chacune de ces protections est appliquée dans l’application, selon un champ ou une route, et reposait sur une hypothèse qui n’a pas tenu. La mesure à prendre est de mettre à jour le logiciel et, en complément, de définir au niveau de l’hôte qui peut accéder au serveur d’inférence et à quelles destinations ce serveur peut se connecter. Ces deux décisions ne dépendent pas de la manière dont vLLM interprète une requête.
Ce qui a été publié
Le 6 octobre 2026, l’avis de sécurité GHSA-h3rc-6mm3-gc2m a été publié. Selon cet avis, une requête multimodale peut définir la valeur code_revision dans le champ mm_processor_kwargs. Lorsque le processor du modèle sur Hugging Face est configuré comme code dynamique (auto_map) et que l’administrateur a activé --trust-remote-code, cette valeur est transmise à AutoProcessor.from_pretrained de Transformers. Elle détermine la version du dépôt depuis laquelle le code du processor sera importé. L’importation d’un module Python revient à exécuter du code arbitraire dans le processus API/renderer de vLLM.
Les principaux points de l’avis :
- Versions : de 0.7.3 jusqu’aux versions antérieures à 0.31.0. Le correctif est inclus dans 0.31.0 et a été intégré par la pull request numéro 58830. L’avis précise que la vulnérabilité a été confirmée sur un commit donné avec Transformers 5.15.0 et 5.16.1, et que la plage des versions touchées n’a pas été vérifiée par recherche binaire (bisect).
- Conditions : un processor dynamique via
auto_map;trust_remote_codeactivé ; le serveur peut télécharger la version choisie dans la requête, ou celle-ci est déjà en cache ; l’attaquant peut créer ou contrôler une version dans le dépôt du modèle servi — selon l’avis, une ref de pull request communautaire sur Hugging Face en est un exemple, lorsque le dépôt le permet ; une requête multimodale atteint le traitement par le processor. Dans la configuration par défaut,trust_remote_code=False, le serveur n’est pas touché. - Authentification : selon l’avis, le composant d’authentification de vLLM ne protège que les routes qui commencent par
/v1,/v2,/inferenceet/cohere. Le champ est aussi accepté dans une requête de tokenisation de chat. La route/tokenizereste donc accessible même lorsque--api-keyest défini, sauf si un proxy externe la protège. - Gravité : l’auteur du signalement a proposé un score de 9.8, mais le niveau publié est High, avec un score CVSS de 8.1 et une complexité d’attaque élevée. Il n’y a pas d’identifiant CVE. Les classifications sont CWE-94 et CWE-829.
- Démonstration : selon l’avis, la démonstration a été exécutée sans connexion réseau, sans GPU, sans poids de modèle et sans modifier de dépôt réel. L’avis ne décrit aucune exploitation en conditions réelles.
Dans le résumé de l’article de LinuxSecurity, la mise à jour est présentée comme contenant des correctifs critiques. Le niveau publié dans l’avis lui-même reste toutefois High.
Le verrouillage de version verrouille les données, pas le code
Quand on verrouille revision, on suppose que cela verrouille tout ce qui accompagne le modèle. Selon l’avis, vLLM verrouillait bien revision et trust_remote_code, mais transmettait les autres paramètres de la requête sans protéger code_revision. Transformers utilise cette valeur pour choisir le code, puis la retire avant de charger les données du processor. Le résultat décrit dans l’avis est le suivant : le code provient de la version contrôlée par l’attaquant, tandis que la configuration et les données restent dans la version verrouillée par l’administrateur. Si vous avez examiné le code avant d’activer l’option, vous avez examiné une version. Cette vulnérabilité en fait exécuter une autre.
L’option --trust-remote-code n’est donc pas un simple paramètre technique à activer pour faire fonctionner un modèle. L’activer, c’est décider d’exécuter du code provenant d’une source externe. La question décisive est de savoir qui choisit ce code. Dans la version vulnérable, c’était la requête.
La clé API protège des préfixes, pas le serveur
On peut facilement supposer que définir --api-key bloque l’accès au serveur pour quiconque ne possède pas de clé. Selon l’avis, la protection est définie par des préfixes de route, dont /tokenize ne fait pas partie. Le guide de sécurité de vLLM la mentionne lui aussi comme une route non protégée. Pour un serveur vLLM accessible sur le réseau, il ne suffit donc pas de vérifier qu’une clé est définie. Il faut vérifier quelles routes sont accessibles sans clé et ce que chacune traite.
Il est donc plus important de vérifier l’exposition du service dans son ensemble que la configuration d’une seule route. Si seuls les services qui ont besoin de vLLM peuvent accéder à son port, les conséquences d’une route laissée hors du mécanisme d’authentification sont fortement réduites.
La possibilité de télécharger fait partie de l’attaque
L’une des conditions mentionnées dans l’avis est que le serveur puisse télécharger la version choisie dans la requête, ou qu’elle soit déjà en cache. Un serveur dont le modèle a été préparé à l’avance n’a pas nécessairement besoin de contacter le dépôt pendant son fonctionnement. Il est possible de bloquer l’accès à toute destination dont il n’a pas besoin. Un point à retenir : si vous laissez l’accès à Hugging Face lui-même, la version contrôlée par l’attaquant se trouve dans le même dépôt. La mise à jour vers 0.31.0 reste donc le correctif.
Il faut aussi vérifier la condition qui concerne le dépôt. Selon l’avis, une ref de pull request communautaire est un exemple de version qu’un attaquant peut contrôler, dans un dépôt qui le permet. En ce sens, un dépôt public ouvert aux contributions est une source de code que vous ne contrôlez pas entièrement.
Le point commun
Dans les trois cas, la protection était appliquée là où l’application interprète les entrées : un champ non protégé, une route non couverte. Ces protections supposent que le développeur ait pensé à chaque champ et à chaque route. À l’inverse, deux questions au niveau de l’hôte ne changent pas lorsqu’un nouveau champ est découvert : qui peut accéder au port du serveur d’inférence, et à quelles destinations son processus peut se connecter. Selon l’avis, le code s’exécute dans le processus API/renderer et porte atteinte à la confidentialité, à l’intégrité et à la disponibilité.
Que faire sur le serveur
- Passez à la version 0.31.0. Vérifiez la version du processus qui reçoit réellement les requêtes, pas seulement l’étiquette de l’image que vous avez téléchargée. Un conteneur déjà en cours d’exécution ne se met pas à jour tout seul.
- Vérifiez si vous avez besoin de
--trust-remote-code. Si le modèle ne l’exige pas, désactivez cette option. S’il l’exige, vérifiez si son processor est configuré comme code dynamique viaauto_map. - Vérifiez le dépôt du modèle servi. Accepte-t-il les pull requests communautaires ? Qui peut y créer des versions ?
- Limitez l’accès réseau au serveur d’inférence. N’exposez pas vLLM directement à Internet. N’autorisez que les services qui en ont besoin. Si un proxy est placé devant lui, vérifiez qu’il exige aussi une authentification pour
/tokenize. Testez ce point avec une requête distincte vers cette route. - Recensez le trafic sortant du serveur d’inférence. Vérifiez à quelles destinations il se connecte pendant son fonctionnement et limitez son accès à celles dont il a réellement besoin.
- Vérifiez ce qui tourne encore. Il est facile d’oublier un serveur de modèles installé pour un test et resté actif avec une ancienne version et l’option activée.
Comment nuDefend peut vous aider
Savoir ce qui tourne. La commande nudefend workloads affiche les outils d’IA qui fonctionnent sur le serveur, y compris les serveurs de modèles, qu’ils s’exécutent directement sur l’hôte ou dans un conteneur. Pour chaque outil, elle affiche les éléments qui ont permis de l’identifier : une unité systemd, un nom d’image ou un motif dans la ligne de commande, par exemple. Cela permet aussi de retrouver un serveur de modèles installé pour un test puis oublié.
Savoir qui peut y accéder. La commande nudefend expose affiche chaque service en écoute sur le serveur et indique qui peut y accéder, du point de vue du serveur lui-même. Elle inclut aussi les services publiés depuis un conteneur. Si un service auparavant limité aux accès internes devient accessible de l’extérieur, nuDefend détecte le changement et le signale. Si le serveur d’inférence est exposé alors que vous ne le souhaitiez pas, vous pouvez bloquer son port depuis le tableau de bord ou la ligne de commande. Avant le blocage, un aperçu montre exactement ce qui sera bloqué. Chaque blocage est consigné et peut être annulé en un clic. Plus de détails sur la page consacrée à l’exposition.
Contrôler ses destinations de connexion. nuDefend consigne les destinations auxquelles chaque outil d’IA du serveur se connecte, le volume de trafic vers chacune et la date de sa première détection. Lorsqu’une destination est identifiée, son nom et son type d’activité sont affichés, par exemple une source de téléchargement de logiciels et de modèles. À partir de ces informations, vous pouvez limiter un outil de votre choix aux destinations que vous avez autorisées. Toute tentative de connexion à une autre destination est bloquée avant que le trafic ne quitte le serveur, et le reste du serveur continue de fonctionner normalement. nuDefend limite aussi les adresses que l’outil peut chercher à résoudre. Chaque requête de résolution bloquée est affichée avec le nom de l’outil. Plus de détails sur la page consacrée au trafic sortant.
Une protection de base. Le trafic vers les adresses IP et les domaines connus comme malveillants, ainsi que le trafic qui en provient, est bloqué par le pare-feu du noyau. Le blocage s’appuie sur des listes de menaces signées, actualisées toutes les 30 minutes. Le tableau de bord affiche un compteur du trafic sortant bloqué. Une valeur supérieure à zéro peut être un premier avertissement : un élément du serveur a tenté de contacter une source malveillante.
En conclusion
La vulnérabilité de vLLM montre que trois protections raisonnables — verrouillage de version, clé API et examen du code — étaient toutes appliquées dans l’application et ont été contournées au moyen d’un seul champ et d’une seule route. Passez à la version 0.31.0 et désactivez --trust-remote-code si vous n’en avez pas besoin. En complément de la mise à jour, définissez au niveau de l’hôte qui peut accéder au serveur d’inférence et à quelles destinations ce serveur peut se connecter. Ces décisions restent en vigueur même lorsqu’un autre champ est découvert.
nuDefend affiche les destinations auxquelles votre serveur de modèles se connecte et permet de limiter un outil de votre choix aux destinations que vous avez autorisées. Toute tentative de connexion à une autre destination est bloquée avant que le trafic ne quitte le serveur.