flawopen.com/AI Security/mcp-python-sdk-oauth-issuer-confusion
Défaut de validation de l’émetteur OAuth dans MCP Python SDK
GHSA-qx49-fqc8-xw99 permettait à un serveur MCP HTTP d’influencer la destination vers laquelle des clients OAuth concernés du SDK Python pouvaient envoyer leurs identifiants. Passez à mcp 1.30.0 ou 2.2.0 ; les fournisseurs machine à machine doivent définir issuer=. Aucun CVE n’est attribué.[1], [3], [8]
Une entreprise envoie une clé scellée à la banque dont l’adresse figure sur une fiche d’annuaire approuvée. Le serveur MCP pouvait fournir cette fiche, et un chemin de secours ne vérifiait pas que l’entrée correspondait à la banque attendue. La validation de l’émetteur compare l’identité de la fiche à la banque configurée comme fiable avant l’envoi de la clé.[1], [2]
Concepts Clés et Termes
Client MCP- Application qui utilise le Model Context Protocol pour accéder aux outils ou données d’un serveur MCP. Cette faille concerne le SDK Python utilisé comme client HTTP avec ses fournisseurs OAuth.[1]
Émetteur du serveur d’autorisation- Identifiant stable du service OAuth qui émet les jetons. Le client doit comparer les métadonnées découvertes à un émetteur attendu indépendamment.[1], [2]
Protected Resource Metadata (PRM)- Métadonnées pouvant identifier les serveurs d’autorisation d’une ressource protégée. Le SDK dispose aussi d’un chemin hérité lorsque PRM est absent.[1], [2]
Correspondances de référentiels
Déroulement de l'Attaque Étape par Étape
Un client HTTP MCP détient des identifiants OAuth
L’application utilise une version affectée de mcp et l’un des fournisseurs OAuth du SDK. Elle peut se connecter à un serveur MCP qui n’est pas entièrement fiable tout en détenant un secret client, une clé de signature ou un enregistrement client stocké.[1]
Le serveur influence la découverte d’autorisation
mcp 1.9.1–1.29.1 ne validait pas l’émetteur et ne liait pas les identifiants sur tous les chemins de découverte. mcp 2.0.0–2.1.1 était concerné en l’absence de PRM avec recours à l’ancien mécanisme de repli, ou lors de la découverte après 403 insufficient_scope. Dans les deux branches, les fournisseurs machine à machine n’avaient pas issuer= pour lier les identifiants préprovisionnés. La plage du paquet indiquée dans l’avis commence à la préversion 2.0.0a1 et est <2.2.0 ; les préversions 2.0.0 concernées sont donc également incluses.[1], [3], [8]
L’impact dépend du fournisseur et des autorisations
Le destinataire peut utiliser les identifiants ou jetons dans les limites des droits accordés. L’avis attribue un CVSS de 7,5 aux fournisseurs sans interaction ; le fournisseur interactif reçoit 6,5, car une personne doit démarrer la connexion. L’avis indique qu’aucun CVE n’a été attribué.[1]
Épingler l’émetteur ferme la faille de confiance
Les versions corrigées déterminent l’émetteur attendu avant la découverte, rejettent les métadonnées divergentes et lient les inscriptions stockées à cet émetteur. Les fournisseurs machine à machine nécessitent également un issuer= explicite.[1], [2], [3], [8]
Matrice des versions et des actions OAuth du MCP Python SDK
| Configuration du client | Condition concernée documentée | Action requise |
|---|---|---|
Client HTTP OAuth avec mcp 1.9.1–1.29.1 | Tous les chemins de découverte manquaient de validation de l’émetteur et de liaison cohérente entre identifiants et émetteur. | Passez à mcp 1.30.0 ou ultérieur et vérifiez la version déployée. |
Client HTTP OAuth avec mcp 2.0.0–2.1.1 | Concerné en l’absence de PRM avec recours à l’ancien mécanisme de repli, ou lors de la découverte après 403 insufficient_scope. La plage du paquet indiquée dans l’avis commence à la préversion 2.0.0a1 et est <2.2.0 ; les préversions 2.0.0 concernées sont donc également incluses. | Passez à mcp 2.2.0 ou ultérieur et vérifiez la version déployée. |
ClientCredentialsOAuthProvider ou PrivateKeyJWTOAuthProvider avec des identifiants préprovisionnés | Les versions concernées ne permettaient pas issuer= pour lier ces identifiants au serveur d’autorisation attendu. | Après la mise à niveau, définissez explicitement issuer=. Migrez depuis RFC7523OAuthClientProvider, obsolète en 1.x. |
| Serveur créé par le SDK, client stdio ou client fournissant son propre jeton/en-tête | Non concerné par cet avis. | Aucune action pour cette faille précise ; réévaluez si l’application utilise aussi un client HTTP OAuth concerné. |
Analyse de Cause Racine
La défaillance de confiance comportait deux éléments liés : certains chemins de découverte concernés ne comparaient pas systématiquement l’issuer des métadonnées du serveur d’autorisation à une valeur attendue indépendamment du serveur MCP, et les identifiants stockés ou préconfigurés n’étaient pas liés à leur émetteur prévu. Un serveur MCP malveillant ou compromis pouvait donc rediriger l’échange de jetons vers un endpoint qu’il contrôlait et recevoir des éléments OAuth détenus par le client. Les correctifs déterminent l’émetteur attendu avant la découverte, rejettent les divergences et lient les enregistrements stockés à cet émetteur ; les fournisseurs machine à machine sans interaction exigent également un issuer= explicite.[1], [2]
Code Source : Vulnérable vs Sécurisé
L’argument issuer est disponible dans les versions corrigées du SDK. Il lie les identifiants machine à machine au serveur d’autorisation prévu ; il ne remplace ni la mise à niveau, ni la suppression des anciennes inscriptions non liées, ni la rotation des identifiants potentiellement exposés.[2], [3], [8]
Extrait source: mcp.client.auth
import os
from mcp.client.auth import ClientCredentialsOAuthProvider
auth = ClientCredentialsOAuthProvider(
client_id=os.environ["MCP_CLIENT_ID"],
client_secret=os.environ["MCP_CLIENT_SECRET"],
)
import os
from mcp.client.auth import ClientCredentialsOAuthProvider
auth = ClientCredentialsOAuthProvider(
client_id=os.environ["MCP_CLIENT_ID"],
client_secret=os.environ["MCP_CLIENT_SECRET"],
issuer=os.environ["MCP_AUTH_ISSUER"],
)
# Utilisez mcp >= 1.30.0 (1.x) ou >= 2.2.0 (2.x).
Liste de Contrôle de Sécurité pour l'Ingénierie
- ✓Inventoriez les clients HTTP MCP et identifiez la version de
mcpinstallée, y compris les dépendances transitives et les images de conteneur.[1] - ✓Mettez à niveau les clients 1.x vers
mcp1.30.0 ou ultérieur, et les clients 2.x vers 2.2.0 ou ultérieur ; vérifiez le fichier de verrouillage et l’environnement déployé.[1], [3], [8] - ✓Définissez
issuer=pourClientCredentialsOAuthProvideretPrivateKeyJWTOAuthProvider; migrez depuis le fournisseur 1.x obsolèteRFC7523OAuthClientProvider, qui ne propose pas cette option.[1], [2] - ✓Supprimez les inscriptions OAuth stockées avant la liaison à l’émetteur, puis réinscrivez-les. Pour une inscription préenregistrée, définissez explicitement son émetteur.[1], [2]
- ✓Conseil de mise en œuvre de FlawOpen : Traitez les serveurs MCP non fiables comme des frontières de confiance qui reçoivent des identifiants. Ne les reliez pas à des clients détenant des secrets OAuth ou clés de production sans autorisation explicite.[1]
- ✓Si un client concerné a pu se connecter à un serveur non fiable, faites tourner son secret client et révoquez les jetons auprès du serveur d’autorisation.[1]
- ✓Ajoutez des tests de régression pour les chemins de découverte 1.x et 2.x, les émetteurs divergents, l’absence de PRM,
403 insufficient_scope, les inscriptions liées à l’émetteur et les fournisseurs machine à machine sansissuer=.[2] - ✓Vérifiez le périmètre de cet avis avant de déclarer d’autres clients concernés : les serveurs créés avec le SDK, les clients stdio et ceux qui fournissent leurs propres jetons ou en-têtes ne sont pas concernés.[1]
Sources
- GitHub-reviewed advisory GHSA-qx49-fqc8-xw99
- MCP Python SDK pull request #3398: Validate issuer on every discovery path
- MCP Python SDK v1.30.0 release notes
- MITRE CWE-345: Insufficient Verification of Data Authenticity
- The Hacker News: Official MCP Python SDK flaw can let malicious servers steal OAuth credentials
- Cycode disclosure: MCP Python SDK OAuth flaw
- CWE-522: Insufficiently Protected Credentials
- MCP Python SDK v2.2.0 release