In this article
Créer des serveurs MCP sécurisés : guide du développeur pour éviter les vulnérabilités critiques
Le Model Context Protocol (MCP) est rapidement devenu la norme de facto pour étendre les capacités de l’IA. Depuis son annonce par Anthropic en novembre 2024, les développeurs se sont empressés de créer des serveurs MCP connectant les LLM aux bases de données, aux systèmes de fichiers, aux API et aux outils en ligne de commande. Toutefois, dans la course au déploiement de l’autonomie de l’IA, la sécurité est souvent passée au second plan, alors que les conséquences prévisibles sur la sécurité sont de plus en plus connues.
La réalité est sans appel : les serveurs MCP ne sont que du code supplémentaire, et plus de code signifie davantage de bugs, une surface d’attaque élargie et un risque accru de vulnérabilités. Le contexte dans lequel ces serveurs fonctionnent rend MCP particulièrement dangereux. Ils s’exécutent sur des machines de développeurs contenant des clés API, des identifiants et de la propriété intellectuelle. Ils sont contrôlés par des agents d’IA susceptibles d’être manipulés par injection de prompt (et injection indirecte de prompt). Enfin, ils sont souvent livrés avec les mêmes catégories de vulnérabilités qui affectent les applications web depuis des décennies.
Ce guide examine les vulnérabilités critiques qui affectent aujourd’hui les serveurs MCP, notamment l’injection de commandes, la traversée de chemin et l’injection SQL, à travers des CVE réelles et des travaux de recherche en sécurité. Vous apprendrez non seulement à reconnaître ces vulnérabilités, mais aussi à créer des serveurs MCP conçus dès le départ pour leur résister.

Injection de commandes : la vulnérabilité MCP la plus répandue
L’injection de commandes est l’une des vulnérabilités les plus fréquemment découvertes dans les serveurs MCP. Le scénario est tristement familier : un serveur MCP lance des commandes système comme npm, git ou lsof et y intègre des entrées contrôlées par l’utilisateur sans les assainir correctement.
On peut l’envisager sous l’angle de l’injection de prompt, mais il s’agit d’un prompt malveillant qui finit par permettre l’exécution de commandes à distance.
Comprendre la vulnérabilité
Prenons l’exemple d’un serveur MCP apparemment inoffensif qui recherche des informations sur des packages npm :
server.tool(
"getNpmPackageInfo",
"Get information about an npm package",
{ packageName: z.string() },
async ({ packageName }) => {
const output = execSync(`npm view ${packageName}`, {
encoding: "utf-8",
});
return {
content: [{ type: "text", text: output }],
};
}
);La vulnérabilité est subtile, mais grave. Le paramètre packageName est directement intégré à une commande shell par interpolation de chaîne. Lorsqu’un utilisateur demande à son assistant IA « Quelle est la date de la dernière version de react; touch /tmp/pwned ? », la commande devient :
npm view react; touch /tmp/pwnedLe point-virgule met fin à la première commande, puis le shell exécute sans problème touch /tmp/pwned comme commande distincte. Un attaquant qui contrôle le prompt peut alors exécuter n’importe quelle commande sur la machine du développeur.
Les prompts deviennent un risque concret de CVE
Ce n’est pas une hypothèse. Plusieurs serveurs MCP se sont révélés vulnérables à l’injection de commandes :
Le GitHub Kanban MCP Server (GHSA-6jx8-rcjx-vmwf) n’assainissait pas les entrées utilisateur, ce qui permettait l’exécution arbitraire de commandes via les opérations Git
Le iOS Simulator MCP Server (GHSA-6f6r-m9pv-67jw) présentait des vulnérabilités similaires
Le package Create MCP Server STDIO contenait des définitions d’outils défectueuses, exposant la surveillance système à des attaques par injection

La solution de sécurité : utiliser l’API sécurisée Node.js execFile au lieu de exec
La fonction Node.js exec lance un shell et y exécute des commandes, ce qui rend dangereux les métacaractères shell comme ;, | et $(). La fonction execFile exécute directement les fichiers, sans interprétation par le shell :
// VULNERABLE: Uses shell interpolation
execSync(`npm view ${packageName}`);
// SECURE: Arguments passed as array, no shell
execFile('npm', ['view', packageName], (error, stdout) => {
// handle result
});Voici une implémentation sécurisée :
import { execFile } from 'child_process';
server.tool(
"getNpmPackageInfo",
{ packageName: z.string() },
async ({ packageName }) => {
return new Promise((resolve, reject) => {
execFile('npm', ['view', packageName], { encoding: 'utf-8' },
(error, stdout) => {
if (error) reject(error);
resolve({
content: [{ type: "text", text: stdout }],
});
}
);
});
}
);Avec execFile, l’entrée malveillante react; touch /tmp/pwned est transmise comme argument unique à npm view. La commande échoue proprement au lieu d’exécuter des commandes arbitraires.
Autres mesures contre l’injection de commandes
Valider les entrées à l’aide de listes d’autorisation : si vous attendez des noms de packages, vérifiez qu’ils respectent les conventions de nommage de npm
Séparer les commandes des arguments : utilisez des API sécurisées contre l’injection de commandes, en privilégiant
execFileplutôt queexecÉchapper les arguments shell : lorsque l’exécution d’un shell est inévitable, utilisez des bibliothèques comme
shell-quoteUtiliser des API spécifiques au langage : au lieu de lancer
npm, utilisez directement l’API du registre npmLimiter la longueur des entrées : des entrées anormalement longues indiquent souvent des tentatives d’injection

Traversée de chemin : sortir du bac à sable
Les serveurs MCP exposent souvent des opérations sur le système de fichiers, par exemple la lecture de fichiers de configuration, l’accès à la documentation ou la diffusion de ressources statiques. Les vulnérabilités de traversée de chemin surviennent lorsque des attaquants manipulent les chemins de fichiers pour accéder à des fichiers hors du répertoire prévu.
Le schéma de vulnérabilité dans le code du serveur MCP
Un schéma courant dans les gestionnaires de ressources MCP :
server.resource(
"pipeline-workflows",
new ResourceTemplate("pipeline-workflows://{name}", { list: undefined }),
async (uri, { name }) => {
const decodedName = decodeURIComponent(name);
let filePath = path.resolve(
process.cwd(),
'.github/workflows/',
`${decodedName}.yaml`
);
const fileContents = readFileSync(filePath, 'utf-8');
return {
contents: [{ uri: uri.href, text: fileContents }]
};
}
);Un attaquant peut demander pipeline-workflows://../../../../../../etc/passwd et le serveur lira consciencieusement /etc/passwd. Sur la machine d’un développeur, les cibles plus intéressantes comprennent :
~/.ssh/id_rsa(clés privées SSH)~/.aws/credentials(clés d’accès AWS)les fichiers
.env(secrets d’application)~/.npmrc(jetons d’authentification npm)
Étude de cas : vulnérabilité de traversée de chemin dans le framework d’IA Mastra
Le package @mastra/mcp-docs-server contenait une vulnérabilité de traversée de chemin subtile. Bien que le code inclue une vérification de sécurité, il poursuivait son exécution même après avoir détecté une tentative de traversée :
async function readMdxContent(docPath, queryKeywords) {
const fullPath = path.resolve(path.join(docsBaseDir, docPath));
// Security check exists...
if (!fullPath.startsWith(path.resolve(docsBaseDir))) {
logger.error(`Path traversal attempt detected`);
return { found: false };
}
// ...
}
execute: async (args) => {
const result = await readMdxContent(path, queryKeywords);
if (result.found) {
return { /* ... */ };
}
// VULNERABILITY: Executes even after traversal detection!
const directorySuggestions = await findNearestDirectory(path, availablePaths);
// ...
}La fonction findNearestDirectory utilisait le chemin non validé, exposant des listes de répertoires hors du périmètre prévu.
Sécuriser la gestion des chemins pour les ressources des serveurs MCP
Appliquez une défense en profondeur aux opérations sur les fichiers :
import path from 'path';
import { accessSync, readFileSync, constants } from 'fs';
function secureFilePath(baseDir, userPath) {
// Normalize and resolve the full path
const normalizedBase = path.resolve(baseDir);
const requestedPath = path.resolve(baseDir, userPath);
// Verify the path stays within bounds
if (!requestedPath.startsWith(normalizedBase + path.sep)) {
throw new Error('Path traversal detected');
}
// Verify the file exists and is readable
accessSync(requestedPath, constants.R_OK);
return requestedPath;
}
server.resource(
"docs",
new ResourceTemplate("docs://{filename}", { list: undefined }),
async (uri, { filename }) => {
const baseDir = path.resolve(process.cwd(), 'docs');
// Halt execution immediately on invalid paths
const safePath = secureFilePath(baseDir, filename);
const contents = readFileSync(safePath, 'utf-8');
return {
contents: [{ uri: uri.href, text: contents }]
};
}
);Liste de contrôle pour prévenir la traversée de chemin
Toujours résoudre les chemins : utilisez
path.resolve()pour convertir les chemins relatifs en chemins absolusValider le résultat : vérifiez que les chemins résolus commencent par le répertoire de base prévu
Rejeter immédiatement : ne poursuivez jamais le traitement après avoir détecté une tentative de traversée
Utiliser des listes d’autorisation : dans la mesure du possible, limitez l’accès aux fichiers explicitement autorisés
Éviter les extensions contrôlées par l’utilisateur : ne laissez pas les utilisateurs spécifier les extensions de fichiers

Injection SQL : l’illusion du « lecture seule »
À mesure que les serveurs MCP se connectent aux bases de données, les vulnérabilités d’injection SQL deviennent un enjeu majeur. Plus insidieusement, de nombreux serveurs MCP pour bases de données proposent un mode « lecture seule » mal conçu, qui donne un faux sentiment de sécurité.
La vérification naïve du mode lecture seule
Plusieurs serveurs MCP pour bases de données tentent d’imposer un accès en lecture seule en vérifiant si les requêtes commencent par SELECT :
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const query = request.params.arguments?.query;
if (!query.trim().toLowerCase().startsWith("select")) {
throw new Error("Only SELECT queries are allowed");
}
const result = await client.query(query);
return { content: [{ type: "text", text: JSON.stringify(result) }] };
});Cette approche échoue de manière catastrophique face au vaste ensemble de fonctions intégrées de PostgreSQL.
Comment contourner les restrictions de lecture seule non sécurisées des serveurs MCP
PostgreSQL expose des fonctions d’administration appelables dans des instructions SELECT :
-- Denial of Service: Terminate database connections
SELECT pg_terminate_backend(pid) FROM pg_stat_activity
WHERE usename != 'admin';
-- Long-running resource exhaustion
SELECT pg_sleep(300);
-- Information disclosure: Expose all running queries
SELECT pid, usename, query FROM pg_stat_activity;
-- Configuration extraction
SELECT name, setting FROM pg_settings WHERE name LIKE '%password%';
-- Force checkpoint (performance disruption)
SELECT pg_start_backup('malicious_backup', true);
Étude de cas : exploitation de vulnérabilités réelles de serveurs MCP par injection SQL
Le serveur MCP de Xata, auquel aucun CVE officiel n’a été attribué, permettait de contourner la transaction en lecture seule du serveur :
COMMIT; INSERT INTO users (name) VALUES ('attacker');En mettant fin à la transaction en lecture seule avec COMMIT, les attaquants pouvaient exécuter des opérations d’écriture.
ExecuteAutomation Database Server (CVE-2025-59333) : des contrôles d’accès tout aussi défectueux permettaient d’enchaîner les requêtes :
SELECT * FROM users; DROP TABLE users; --Comment sécuriser l’accès aux bases de données des serveurs MCP ?
Imposer l’exécution d’une seule requête :
// Force prepared statement (prevents multi-query)
const result = await client.query({
name: "single-query",
text: userQuery,
values: [],
});Utiliser les autorisations au niveau de la base de données :
-- Create restricted user
CREATE USER mcp_readonly WITH PASSWORD 'secure_password';
-- Grant only SELECT on specific tables
GRANT SELECT ON public.users TO mcp_readonly;
GRANT SELECT ON public.products TO mcp_readonly;
-- Revoke dangerous function access
REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA pg_catalog FROM mcp_readonly;Mettre en place une liste d’autorisation des requêtes :
const ALLOWED_QUERIES = {
"get_users": "SELECT id, name FROM users WHERE active = $1",
"get_products": "SELECT id, name, price FROM products WHERE category = $1"
};
server.tool("query_database", { queryName: z.string(), params: z.array(z.any()) },
async ({ queryName, params }) => {
const query = ALLOWED_QUERIES[queryName];
if (!query) throw new Error("Query not permitted");
return await client.query(query, params);
}
);Bloquer les fonctions dangereuses :
const DANGEROUS_PATTERNS = [
/pg_\w+\(/i, // PostgreSQL system functions
/\bcopy\b/i, // COPY command
/\binto\s+outfile/i, // File operations
/\bload_file\b/i, // File loading
/\bexecute\b/i, // Dynamic execution
/\bsleep\b/i, // Sleep-based DoS
];
function validateQuery(query) {
for (const pattern of DANGEROUS_PATTERNS) {
if (pattern.test(query)) {
throw new Error("Query contains prohibited patterns");
}
}
}Utiliser les ressources MCP pour un accès structuré :
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const resourceUrl = new URL(request.params.uri);
const [schema, tableName] = resourceUrl.pathname.split('/').filter(Boolean);
// Validate against allowlist
if (!ALLOWED_TABLES.includes(tableName)) {
throw new Error('Table not permitted');
}
// Use parameterized query
const result = await client.query(
'SELECT * FROM information_schema.columns WHERE table_name = $1',
[tableName]
);
return { contents: [{ uri: request.params.uri, text: JSON.stringify(result) }] };
});
Sécuriser les dépendances tierces
Les serveurs MCP s’appuient sur des packages npm pour les pilotes de base de données, les clients HTTP et les fonctions utilitaires. Chaque dépendance représente un risque potentiel pour la chaîne d’approvisionnement :
Dépendances vulnérables : CVE connues dans des packages comme
pg,axiosoulodashPackages malveillants : attaques par typosquattage ciblant les développeurs de serveurs MCP
Vulnérabilités transitives : failles de sécurité dans les dépendances des dépendances
Stratégies d’atténuation
Analyse continue : intégrez Snyk ou des outils similaires à votre pipeline CI/CD pour détecter les dépendances vulnérables avant le déploiement
Épingler les versions des dépendances : utilisez des fichiers de verrouillage (
package-lock.json) et vérifiez leur intégritéMises à jour régulières : surveillez les avis de sécurité et appliquez rapidement les correctifs
Réduire le nombre de dépendances : chaque package élargit la surface d’attaque ; évaluez la nécessité de chaque package dans votre serveur MCP.
Auditer avant l’adoption : consultez les scores de santé des packages dans la Snyk Security Database
Mesures recommandées : créer des serveurs MCP sécurisés

Pour les développeurs de serveurs MCP
Valider toutes les entrées : ne faites jamais confiance aux données issues d’appels d’outils générés par un LLM
Utiliser des API sécurisées : préférez
execFileàexec, et les requêtes paramétrées à l’interpolation de chaînesMettre en place une défense en profondeur : multipliez les couches de validation plutôt que de vous fier à une seule vérification
Intégrer des outils SAST : utilisez Snyk Code ou un outil similaire pour détecter les vulnérabilités pendant le développement
Appliquer le principe du moindre privilège : limitez au minimum les droits des utilisateurs de bases de données, l’accès au système de fichiers et les autorisations réseau
Documenter le modèle de sécurité : indiquez clairement les limites de confiance et les hypothèses
Pour les utilisateurs de serveurs MCP
Auditer avant l’installation : examinez le code source, en particulier les gestionnaires d’outils
Surveiller le comportement : soyez attentif aux accès inattendus aux fichiers, aux requêtes réseau ou à l’exécution de commandes
Isoler les environnements : exécutez les serveurs MCP dans des environnements en bac à sable
Maintenir les systèmes à jour : appliquez rapidement les correctifs de sécurité
Signaler les vulnérabilités : adoptez une démarche de divulgation responsable lorsque vous découvrez un problème
Pour les organisations
Établir des politiques MCP : définissez les serveurs approuvés et les exigences de sécurité
Mettre en place des analyses : analysez automatiquement le code des serveurs MCP avant leur déploiement
Former les développeurs : sensibilisez les équipes aux bonnes pratiques de développement sécurisé pour MCP
Surveiller et auditer : consignez l’activité des serveurs MCP à des fins d’examen de sécurité
La voie à suivre
Le Model Context Protocol constitue un puissant moyen d’étendre les capacités de l’IA, mais tout pouvoir implique des responsabilités. Les vulnérabilités examinées ici — injection de commandes, traversée de chemin et injection SQL — ne sont pas nouvelles. Ce sont les mêmes failles qui affectent les applications web depuis des décennies, désormais présentes dans un nouveau contexte et susceptibles d’avoir un impact plus important.
L’ironie est frappante : dans notre empressement à rendre l’IA plus performante, nous l’avons connectée à des systèmes reposant sur les mêmes fondations fragiles que nous tentons de sécuriser depuis des années. Un LLM connecté à un serveur MCP vulnérable devient un vecteur d’attaque involontaire, dont l’intelligence est détournée par de simples manipulations de chaînes.

Pour aller de l’avant, il faut traiter le développement des serveurs MCP avec la même rigueur que tout code critique pour la sécurité. Validez les entrées. Utilisez des API sécurisées. Mettez en place une défense en profondeur. Recherchez les vulnérabilités. Et n’oubliez pas que le « lecture seule » est souvent une dangereuse illusion.
Nous disposons des outils nécessaires pour créer des serveurs MCP sécurisés. La question est de savoir si nous les utiliserons avant que la prochaine vague de vulnérabilités ne fasse la une.
Vous souhaitez comprendre les nouvelles voies d’attaque ciblant les serveurs MCP, de l’empoisonnement et du détournement des outils aux flux toxiques ? Analysez des incidents réels et découvrez comment vous défendre grâce à des stratégies pratiques tenant compte des flux dans l’eBook Securing The MCP Servers Ecosystem.
Participez à Fetch the Flag 2026 !
Mettez vos compétences à l’épreuve, relevez les défis et grimpez en tête du classement. Rejoignez-nous du 12 février à midi (ET) au 13 février à midi (ET) pour l’événement CTF ultime.