FlawOpen

flawopen.com/AI Security/mcp-python-sdk-oauth-issuer-confusion

● CWE-345 / CWE-522 · CVSS 7.5[1] · Haute
Recherche · FlawOpen

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]

Vecteur CVSSCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N[1]
💡 Explication en Langage Simple (ELI5)

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]
Vérificateur PKCE
Secret créé par le client pour lier l’échange du code OAuth au client ayant démarré le flux. Il ne doit pas être divulgué à un endpoint non fiable.[1], [2]
CWE-345 / CWE-522
Vérification insuffisante de l’authenticité des données : des données sont acceptées sans établir correctement qu’elles proviennent de la source attendue. CWE-522 décrit la protection insuffisante des identifiants.[1], [4], [7]

Correspondances de référentiels

  • Référence CWE pour la faiblesse décrite dans ce guide : CWE-345 CWE-522

Déroulement de l'Attaque Étape par Étape

Étape 1

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]

Étape 2

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]

Étape 3

Le point de terminaison de jeton reçoit les identifiants

Le client peut envoyer un secret client, un code d’autorisation et le vérificateur PKCE, ou une assertion signée, à un endpoint choisi via ces métadonnées. L’identifiant dépend du fournisseur ; tous les clients ne sont pas concernés.[1], [2]

Étape 4

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]

Étape 5

É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 clientCondition concernée documentéeAction requise
Client HTTP OAuth avec mcp 1.9.1–1.29.1Tous 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.1Concerné 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ésLes 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êteNon 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

IMPLÉMENTATION VULNÉRABLE
import os
from mcp.client.auth import ClientCredentialsOAuthProvider

auth = ClientCredentialsOAuthProvider(
    client_id=os.environ["MCP_CLIENT_ID"],
    client_secret=os.environ["MCP_CLIENT_SECRET"],
)
PATCH SÉCURISÉ ET ROBUSTE
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

Sources

← AI Security Directory →