> ## Content Index
> Fetch the complete content index at: https://www.sfeir.dev/llms.txt
> Use this file to discover other available public pages before exploring further.

# mcp-posture : un scanner open source pour auditer la sécurité OAuth d'un serveur MCP
- URL: https://www.sfeir.dev/ia/mcp-posture-scanner-securite-mcp/
- Published: 2026-11-12T15:11:20.000Z
- Updated: 2026-11-12T15:11:20.000Z
- Description: mcp-posture audite un serveur MCP distant : OAuth, PKCE, TLS, tool poisoning, rug pulls. Scan passif, sortie SARIF, GitHub Action.
- Author: Baptiste PIRAULT
- Tags: IA, MCP, Agent IA, Agentic, Sécurité, Sécurité LLM, Cybersecurité

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/8%5Fmcp-posture%2C%20scanner%20de%20s%C3%A9curit%C3%A9%20OAuth%20pour%20serveurs%20MCP%20%28FR%29.md?ref=sfeir.dev#mcp-posture--un-scanner-open-source-pour-auditer-la-s%C3%A9curit%C3%A9-oauth-dun-serveur-mcp)

En déployant des serveurs MCP en production, j'ai accumulé une liste de failles qui passaient tous les tests fonctionnels : un écran de consentement désactivé qui ouvrait la porte à un *confused deputy*, un chemin `/mcp..%2f` qui contournait l'authentification, un `issuer` incohérent, des métadonnées servies à la mauvaise URL. Chaque fois, la vérification tenait en quelques requêtes HTTP. J'ai donc décidé de les automatiser. Le résultat s'appelle **mcp-posture**, un scanner open source (licence MIT) de la posture de sécurité des serveurs MCP distants.

## Pourquoi un scanner de sécurité MCP de plus ?

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/8%5Fmcp-posture%2C%20scanner%20de%20s%C3%A9curit%C3%A9%20OAuth%20pour%20serveurs%20MCP%20%28FR%29.md?ref=sfeir.dev#pourquoi-un-scanner-de-s%C3%A9curit%C3%A9-mcp-de-plus-)

Plusieurs bons scanners MCP existent déjà (Snyk agent-scan, Cisco mcp-scanner, Ramparts...). Ils se concentrent surtout sur les configurations locales des clients et les serveurs stdio. Il manquait un outil qui regarde un serveur **distant** comme un attaquant le voit : de l'extérieur, avant de se connecter.

- Comment le serveur challenge-t-il un appel non authentifié ?
- Quel serveur d'autorisation désigne-t-il, et ses métadonnées sont-elles cohérentes ?
- PKCE S256 est-il imposé ? Les flux implicit et password sont-ils désactivés ?
- Les descriptions de tools cachent-elles des instructions ?
- Les tools ont-ils changé depuis la dernière approbation ?

Chaque constat cite la section de la spécification concernée et la révision MCP à laquelle il s'applique, de `2025-03-26` à `2026-07-28`.

## Démarrage en une commande

```
# sans installation
uvx mcp-posture scan https://mcp.example.com/mcp
```

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/8%5Fmcp-posture%2C%20scanner%20de%20s%C3%A9curit%C3%A9%20OAuth%20pour%20serveurs%20MCP%20%28FR%29.md?ref=sfeir.dev#d%C3%A9marrage-en-une-commande)

Exemple de sortie sur un serveur de démonstration volontairement mal configuré :

```
http://127.0.0.1:8402/mcp
  spec 2025-06-18 (negotiated) · transport streamable-http · auth not required
critical MCPP-ASM04   code_challenge_methods_supported ['plain'] does not include S256.
high     MCPP-ASM02   Metadata declares issuer 'http://127.0.0.1:8402/', expected 'http://127.0.0.1:8402'.
high     MCPP-AUTHN01 tools/list answered without credentials (2 tool(s) including delete_note).
high     MCPP-PRM06   bearer_methods_supported includes `query`.
high     MCPP-TRN08   Session IDs look sequential (numerically close across two sessions).
medium   MCPP-PRM03   resource '.../mcp/' does not match '.../mcp' (trailing slash only).

```

On y retrouve des erreurs bien réelles : un slash final qui suffit à casser la correspondance stricte du `resource`, un issuer qui diffère d'un caractère de l'URL de récupération, un `tools/list` accessible sans authentification qui expose un `delete_note`.

## Ce qu'il vérifie : OAuth, TLS, tool poisoning et rug pulls

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/8%5Fmcp-posture%2C%20scanner%20de%20s%C3%A9curit%C3%A9%20OAuth%20pour%20serveurs%20MCP%20%28FR%29.md?ref=sfeir.dev#ce-quil-v%C3%A9rifie--oauth-tls-tool-poisoning-et-rug-pulls)

Environ 70 vérifications réparties en familles, chacune avec un identifiant stable :

- **TRN** (transport) : HTTPS, version et certificat TLS, HSTS, redirection HTTP vers HTTPS, ancien transport SSE, entropie des identifiants de session.
- **AUTHN** : `tools/list` anonyme, challenge Bearer, découverte `resource_metadata`, fuites d'information dans les erreurs.
- **PRM** : Protected Resource Metadata ([RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728?ref=sfeir.dev)), correspondance exacte du `resource`, tokens en query string, scopes.
- **ASM** : métadonnées du serveur d'autorisation ([RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414?ref=sfeir.dev)), égalité de l'issuer, PKCE S256 ou `plain`, flux implicit et password, Dynamic Client Registration ouvert, paramètre `iss` ([RFC 9207](https://datatracker.ietf.org/doc/html/rfc9207?ref=sfeir.dev)).
- **CIMD** : support des Client ID Metadata Documents, et `cimd lint` pour valider le document de votre propre client.
- **SCP** : scopes trop larges, cohérence entre métadonnées.
- **TOOL** : texte qui ressemble à des instructions (y compris en français), Unicode invisible ou bidirectionnel, blobs encodés, URLs d'exfiltration, noms confondables, *shadowing* d'autres tools, annotations manquantes.
- **PIN** : détection des *rug pulls* par comparaison avec une liste de tools épinglée.

```
mcp-posture checks list               # le catalogue complet
mcp-posture checks show MCPP-ASM04    # justification, remédiation, références
```

## Passif par défaut

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/8%5Fmcp-posture%2C%20scanner%20de%20s%C3%A9curit%C3%A9%20OAuth%20pour%20serveurs%20MCP%20%28FR%29.md?ref=sfeir.dev#passif-par-d%C3%A9faut)

C'était un choix de conception non négociable : un scan par défaut n'envoie que des `GET` de métadonnées, le handshake MCP standard, les appels de liste, et quelques sondes en lecture seule (HTTP simple, chemin inexistant, handshakes TLS). **Aucun tool n'est jamais appelé.**

Conséquence assumée : ce qu'il faudrait une sonde active pour confirmer (validation de l'`Origin`, vérification de l'audience des tokens, application effective de PKCE) est explicitement listé comme *non vérifié*. Je préfère un rapport qui dit ce qu'il ne sait pas à un rapport qui devine.

## Un scanner qui se méfie de sa cible

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/8%5Fmcp-posture%2C%20scanner%20de%20s%C3%A9curit%C3%A9%20OAuth%20pour%20serveurs%20MCP%20%28FR%29.md?ref=sfeir.dev#un-scanner-qui-se-m%C3%A9fie-de-sa-cible)

Un scanner qui se connecte à des serveurs arbitraires est lui-même une surface d'attaque. mcp-posture traite chaque cible comme potentiellement hostile :

- adresses privées, loopback et metadata cloud refusées **après résolution DNS** et à chaque saut de redirection (protection SSRF) ;
- taille des réponses et décompression plafonnées ;
- identifiants jamais transmis d'une origine à une autre ;
- tokens lus uniquement depuis une variable d'environnement, un fichier ou stdin, et masqués partout ;
- texte contrôlé par le serveur neutralisé dans les rapports (caractères de contrôle, bidi, invisibles) avant d'atteindre votre terminal ou vos commentaires de PR ;
- aucune télémétrie.

## Scanner ses serveurs MCP dans la CI (GitHub Action, SARIF)

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/8%5Fmcp-posture%2C%20scanner%20de%20s%C3%A9curit%C3%A9%20OAuth%20pour%20serveurs%20MCP%20%28FR%29.md?ref=sfeir.dev#scanner-ses-serveurs-mcp-dans-la-ci-github-action-sarif)

Le cas d'usage qui m'intéressait le plus : vérifier en continu la liste des serveurs autorisés d'une organisation.

```
- uses: batou9150/mcp-posture@v1.1.1
  with:
    targets-file: mcp-servers.txt
    fail-on: high
```

Les résultats sortent en **SARIF 2.1.0**, ancrés à la ligne du fichier qui déclare chaque cible, pour un affichage direct dans GitHub code scanning. Le rapport Markdown alimente le résumé du job.

Pour la détection de *rug pull*, on épingle l'état approuvé, puis chaque scan compare :

```
mcp-posture pin https://mcp.example.com/mcp    # crée mcp-posture.lock.json
```

Les exceptions passent par un fichier `.mcp-posture-ignore` où **chaque suppression exige une justification et une date d'expiration**. Une exception expirée réapparaît dans le rapport.

```
[[ignore]]
check = "MCPP-ASM09"
target = "https://mcp.example.com/*"
justification = "DCR ouvert volontaire : registre de clients public"
expires = 2026-12-31
```

## Pour aller plus loin

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/8%5Fmcp-posture%2C%20scanner%20de%20s%C3%A9curit%C3%A9%20OAuth%20pour%20serveurs%20MCP%20%28FR%29.md?ref=sfeir.dev#pour-aller-plus-loin)

- **Scanner les serveurs de vos postes :** `mcp-posture discover` puis `scan --from-client-config auto` lisent les configurations de Claude, VS Code, Cursor, Windsurf et `.mcp.json`.
- **Lister les tools derrière authentification :** `--login` ouvre un flux OAuth avec PKCE dans le navigateur, sans jamais afficher le token.
- **Docker :** image distroless, non-root, signée avec cosign (`ghcr.io/batou9150/mcp-posture`).
- **Claude Code :** un plugin ajoute une revue sémantique des descriptions de tools et des snippets de remédiation pour les serveurs d'autorisation et gateways courants.

```
/plugin marketplace add batou9150/mcp-posture
/plugin install mcp-posture@mcp-posture

```

Une précision importante : mcp-posture **rapporte, il ne bloque pas**. Pour l'application des règles au runtime, associez-le à un proxy comme [mcp-context-protector](https://github.com/trailofbits/mcp-context-protector?ref=sfeir.dev). Et ne scannez que des serveurs qui vous appartiennent ou que vous êtes autorisé à tester.

## Conclusion

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/8%5Fmcp-posture%2C%20scanner%20de%20s%C3%A9curit%C3%A9%20OAuth%20pour%20serveurs%20MCP%20%28FR%29.md?ref=sfeir.dev#conclusion)

Chaque vérification de mcp-posture correspond à une erreur que j'ai commise ou vue en production. L'objectif est simple : qu'aucune équipe n'ait à redécouvrir ces failles à la main. Le projet est sur [GitHub](https://github.com/batou9150/mcp-posture?ref=sfeir.dev) et [PyPI](https://pypi.org/project/mcp-posture/?ref=sfeir.dev), et les contributions sont bienvenues, notamment pour les vérifications actives encore en chantier. Lancez-le sur vos serveurs : le premier scan est rarement vide.