Choisir le bon mécanisme d’authentification ne suffit pas.
Une intégration peut utiliser une API key parfaitement adaptée à son modèle d’identité et rester vulnérable si cette clé est stockée au mauvais endroit, partagée avec la configuration ou impossible à renouveler proprement.
La gestion des secrets doit donc être pensée comme un problème à part entière.
Le module structure cette gestion autour de trois pratiques complémentaires :
- Separation
- Storage
- Rotation
Ces trois mécanismes répondent à trois questions différentes :
Separation
→ Le secret est-il séparé de la configuration ?
Storage
→ Où vit réellement la valeur ?
Rotation
→ Peut-on remplacer cette valeur proprement ?
C’est cette combinaison qui permet de passer d’un credential simplement fonctionnel à un credential réellement exploitable en production.
1. Separation : le credential ne doit jamais voyager avec la configuration
Le premier principe est le plus important :
A credential never travels with the configuration that references it.
Autrement dit :
configuration
≠
secret
Une configuration doit contenir uniquement la référence nécessaire pour retrouver le credential au runtime.
Elle ne doit pas contenir sa valeur réelle.
Le mauvais pattern
Prenons un .mcp.json contenant directement une API key :
{
"type": "http",
"url": "https://warehouse.internal/mcp",
"headers": {
"Authorization": "Bearer sk-prod-warehouse-abc123"
}
}
Le problème est mécanique.
Les fichiers de configuration sont souvent :
- commités ;
- partagés ;
- copiés ;
- clonés ;
- envoyés dans des pipelines ;
- conservés dans des sauvegardes.
Si le secret est écrit inline, il suit exactement le même chemin.
On obtient :
.mcp.json
↓
repository
↓
clone développeur
↓
clone CI
↓
autres copies
La clé voyage avec le fichier.
Le bon pattern
La configuration doit contenir uniquement une référence :
{
"type": "http",
"url": "https://warehouse.internal/mcp",
"headers": {
"Authorization": "Bearer ${WAREHOUSE_MCP_TOKEN}"
}
}
Le fichier sait quel secret demander, mais il ne contient pas ce secret.
On sépare ainsi :
.mcp.json
↓
${WAREHOUSE_MCP_TOKEN}
environnement / secret store
↓
valeur réelle
Le projet reste partageable sans transporter le credential.
Pourquoi cette séparation est si importante avec Git
Le cas étudié dans le module montre qu’une clé placée dans .mcp.json puis commitée entre immédiatement dans l’historique du repository.
Même si le développeur la retire plus tard, l’ancien commit existe toujours.
C’est pourquoi le problème ne se résume pas à :
« La clé est actuellement visible dans le fichier. »
Il faut se demander :
« Cette valeur a-t-elle déjà été enregistrée dans un système qui conserve un historique ? »
Si la réponse est oui, il faut considérer le credential comme compromis.
Supprimer la valeur ne la rend pas secrète à nouveau
C’est une règle essentielle :
Un secret exposé ne redevient pas secret simplement parce qu’on l’a supprimé.
Une fois la valeur copiée, clonée ou enregistrée dans un historique, on ne sait plus qui a pu la récupérer.
La correction ne consiste donc pas seulement à remettre le secret au bon endroit.
Elle doit aussi inclure une rotation.
Nous y reviendrons.
2. Storage : où doit vivre la valeur ?
Une fois la configuration séparée du credential, une deuxième question apparaît :
Où stocker la véritable valeur ?
Le module distingue principalement deux cas :
- environment variable ;
- secret store.
Le choix dépend du nombre de consommateurs, du niveau d’audit attendu et de la durée de vie du secret.
Environment variable : adaptée aux usages locaux ou injectés
Une environment variable est suffisante lorsqu’un credential existe uniquement :
- sur une machine ;
- pendant une exécution ;
- dans un pipeline CI ;
- ou dans un contexte limité.
Le principe est :
Environment
↓
WAREHOUSE_MCP_TOKEN
↓
MCP configuration
↓
MCP server
La configuration ne voit que le nom de la variable.
La valeur est fournie au runtime.
Exemple en CI
Dans un pipeline, il est préférable que le runner injecte le secret directement dans l’environnement.
Par exemple :
CI secret
↓
environment variable
↓
WAREHOUSE_MCP_TOKEN
↓
.mcp.json
Le credential n’a pas besoin d’être écrit dans un fichier local du runner.
Le module présente explicitement cette approche comme préférable à un fichier de credentials tel que :
/home/jenkins/.config/mcp-credentials.json
si celui-ci contient directement la valeur du secret.
Secret store : pour les secrets partagés ou audités
Lorsqu’un même credential est utilisé par plusieurs personnes ou services, une simple environment variable locale devient moins adaptée.
Le module recommande alors un secret store.
Un secret store permet de :
- centraliser la valeur ;
- fournir le secret uniquement aux consommateurs autorisés ;
- enregistrer les accès ;
- réduire le nombre de copies ;
- faciliter la rotation.
On passe d’une architecture distribuée :
Secret
├── fichier service A
├── fichier service B
├── machine développeur
├── CI
└── autre intégration
à une architecture centralisée :
Secret store
/ | \
/ | \
Service A Service B CI
Les consommateurs demandent la valeur lorsqu’ils en ont besoin.
Environment variable ou secret store ?
Le module propose un raisonnement simple.
Environment variable
À privilégier lorsqu’un secret :
- est local ;
- est temporaire ;
- n’a qu’un nombre limité de consommateurs ;
- est injecté au moment de l’exécution.
Secret store
À privilégier lorsqu’un secret :
- est partagé ;
- doit être audité ;
- doit être géré centralement ;
- est utilisé par plusieurs services ;
- doit pouvoir être rotated sans multiplier les modifications.
On peut résumer ainsi :
| Contexte | Solution |
|---|---|
| Machine locale | Environment variable |
| Pipeline CI | Environment variable |
| Exécution temporaire | Environment variable |
| Plusieurs services | Secret store |
| Audit des accès requis | Secret store |
| Gestion centralisée | Secret store |
3. Rotation : remplacer le credential sans casser le système
La troisième pratique est la rotation.
La rotation consiste à remplacer un credential existant par une nouvelle valeur.
Elle doit être réalisée :
- régulièrement ;
- immédiatement après toute suspicion d’exposition.
Le principe est simple :
ancienne clé
↓
révocation
↓
nouvelle clé
Mais la facilité avec laquelle cette opération peut être réalisée dépend directement de la qualité de l’architecture précédente.
Pourquoi la rotation devient coûteuse avec des secrets hardcodés
Imaginons une clé écrite directement dans plusieurs fichiers :
service A
→ sk-prod-warehouse-abc123
service B
→ sk-prod-warehouse-abc123
pipeline
→ sk-prod-warehouse-abc123
script local
→ sk-prod-warehouse-abc123
Pour effectuer une rotation, il faut retrouver chaque copie.
Puis modifier chaque système.
Le risque est important :
rotation
↓
service oublié
↓
panne
C’est exactement ce qui s’est produit dans le scénario du module : deux services externes utilisaient encore la même clé et ont cessé de fonctionner au moment de la rotation.
Pourquoi la séparation facilite la rotation
Avec une variable ou un secret store, le code dépend du nom, pas de la valeur.
Par exemple :
WAREHOUSE_MCP_TOKEN
La configuration continue à utiliser ce nom avant et après la rotation.
Seule la valeur derrière change.
avant
WAREHOUSE_MCP_TOKEN
→ ancienne clé
après
WAREHOUSE_MCP_TOKEN
→ nouvelle clé
Le code ne change pas.
La configuration ne change pas.
Cette séparation rend la rotation beaucoup moins coûteuse.
Un secret compromis doit être rotated immédiatement
Le module insiste sur ce point :
Rotation is the only appropriate response to a leaked key.
Pourquoi ?
Parce qu’un secret exposé ne peut pas être rendu secret à nouveau.
Même si l’on supprime le fichier qui contenait la clé, rien ne garantit que la valeur n’a pas déjà été :
- copiée ;
- enregistrée ;
- clonée ;
- sauvegardée ;
- récupérée depuis l’historique Git.
La seule réponse fiable consiste à invalider l’ancienne valeur.
Rotation planifiée et rotation d’incident
Il faut distinguer deux situations.
Rotation planifiée
Le credential est renouvelé régulièrement selon une politique définie.
Rotation après exposition
La rotation doit être déclenchée immédiatement dès qu’une compromission est suspectée.
Le second cas est une réponse à incident.
Il ne faut pas attendre la prochaine date prévue.
Le rôle du least privilege
La gestion des secrets ne s’arrête pas au stockage et à la rotation.
Le module rappelle qu’un credential doit être limité au narrowest access its task needs.
C’est le principe du least privilege.
Imaginons :
Credential A
→ accès complet au warehouse
Credential B
→ lecture limitée à quelques datasets
Si les deux permettent au MCP server d’effectuer sa tâche, le credential B est préférable.
En cas de compromission :
Credential B compromis
↓
blast radius limité
Le credential ne doit donc pas seulement être secret.
Il doit aussi être peu puissant.
Ne pas réutiliser le même credential partout
Le scénario du module montre également le danger d’un credential partagé entre plusieurs systèmes.
Une même clé était utilisée par plusieurs services.
Lorsqu’elle a été rotated, plusieurs intégrations ont cassé.
Cela révèle une dépendance excessive.
Plus un credential est partagé :
plus de consommateurs
↓
plus grand blast radius
↓
rotation plus difficile
L’objectif est donc de limiter le nombre de systèmes dépendant du même secret.
Maintenir un inventaire des consommateurs
Le document recommande de conserver une trace des systèmes qui utilisent chaque credential.
Cette information devient essentielle lors d’une rotation.
Sans inventaire :
rotate key
↓
découverte progressive
des services cassés
Avec un inventaire :
credential
↓
liste des consommateurs
↓
rotation planifiée
La rotation devient une opération maîtrisée plutôt qu’un diagnostic de panne.
Le cas CI : diagnostiquer le vrai problème
Le module fournit une trace de connexion MCP :
[MCP Client] Connecting to https://data-api.internal/mcp ...
[MCP Client] GET /auth/token, 401 Unauthorized
[MCP Client] Reading credential from:
/home/jenkins/.config/mcp-credentials.json
[MCP Client] Credential value:
WAREHOUSE_TOKEN=sk-****[redacted]
[MCP Client] Retrying with credential, 401 Unauthorized
[MCP Client] Connection failed after 3 attempts
Une lecture superficielle pourrait conclure :
« Il faut simplement remplacer la clé rejetée dans le fichier. »
Mais cela ne corrige qu’un symptôme.
Le problème architectural reste présent :
credential
↓
stocké dans un fichier
La correction ciblée
Le module recommande la séquence suivante :
- rotate the rejected key ;
- remove the credential from the file ;
- inject the credential as an environment variable in the CI runner ;
- update the MCP configuration to reference that variable.
On corrige donc simultanément :
credential invalide
+
mauvais stockage
C’est un raisonnement important pour l’examen : la meilleure correction n’est pas toujours celle qui rétablit le fonctionnement le plus rapidement.
Il faut aussi supprimer la cause structurelle.
Pourquoi passer à OAuth n’est pas automatiquement la bonne réponse
Dans ce scénario, une autre solution possible serait :
remplacer l’API key par OAuth.
Mais le module montre que ce choix ne découle pas de la trace.
Rien n’indique que le service doive utiliser une user identity.
S’il fonctionne légitimement avec une service identity, une API key reste appropriée.
Le problème est son stockage et sa rotation.
Il faut donc éviter le raisonnement :
problème d'API key
↓
OAuth forcément meilleur
Le bon raisonnement est :
Quel modèle d'identité ?
↓
Quel mécanisme adapté ?
↓
Où stocker le credential ?
↓
Comment le rotate ?
Protéger la configuration contre les secrets inline
Le document ne s’arrête pas à la gestion manuelle des secrets.
Il recommande également d’empêcher Claude Code d’inscrire directement un credential dans .mcp.json.
Deux niveaux sont proposés.
CLAUDE.md : communiquer la politique
Une règle peut être ajoutée dans CLAUDE.md :
Never write credential values inline in .mcp.json.
Use environment variable references instead.
Cela permet au modèle de connaître la convention du projet.
Mais cette règle reste une instruction.
Elle n’est pas une garantie technique.
PreToolUse hook : faire respecter la politique
Pour renforcer la sécurité, un PreToolUse hook peut inspecter les opérations d’écriture ou d’édition.
Le principe est :
Claude veut écrire .mcp.json
↓
PreToolUse
↓
recherche de pattern credential
/ \
oui non
↓ ↓
block allow
Le module explique que ce mécanisme permet de bloquer l’opération avant son exécution.
Instruction vs enforcement
Cette distinction est fondamentale :
CLAUDE.md
→ dit ce qui doit être fait
Hook
→ contrôle ce qui peut être fait
Pour une convention de style, une instruction peut suffire.
Pour empêcher une fuite de credential, un contrôle déterministe est plus approprié.
Les trois pratiques réunies
On peut maintenant assembler le modèle complet.
Separation
.mcp.json
→ référence
→ ${WAREHOUSE_MCP_TOKEN}
Storage
usage local / CI
→ environment variable
usage partagé / auditable
→ secret store
Rotation
exposition
→ révoquer
→ nouvelle valeur
Le tout complété par :
least privilege
+
inventaire des consommateurs
+
enforcement via hook
La chaîne de sécurité complète
Une architecture plus robuste ressemble donc à ceci :
.mcp.json
↓
référence variable
↓
environment variable / secret store
↓
credential limité
↓
MCP server
Autour de cette chaîne :
CLAUDE.md
→ convention
PreToolUse hook
→ blocage des credentials inline
rotation policy
→ renouvellement
inventory
→ connaissance des consommateurs
La sécurité ne dépend ainsi plus d’une seule bonne pratique.
Elle repose sur plusieurs couches complémentaires.
Ce qu’il faut retenir pour la certification
Pourquoi séparer secret et configuration ?
Parce que les fichiers de configuration sont souvent commités, partagés et clonés.
Le secret ne doit pas suivre ces copies.
Une clé a été supprimée d’un commit ultérieur : est-elle sûre ?
Non.
Elle reste potentiellement présente dans l’historique Git.
Elle doit être considérée comme compromise et rotated.
Environment variable ou secret store ?
Environment variable pour un secret local, temporaire ou injecté dans un pipeline.
Secret store pour un secret partagé, centralisé ou devant être audité.
Pourquoi la rotation est-elle plus simple avec une variable ?
Parce que le code dépend du nom de la variable, pas de la valeur du credential.
Une clé exposée peut-elle être remise en sécurité sans rotation ?
Non.
Une valeur exposée ne peut pas redevenir secrète.
Pourquoi limiter les permissions du credential ?
Pour réduire le blast radius en cas de compromission.
Pourquoi inventorier les consommateurs ?
Pour éviter que la rotation ne casse des systèmes inconnus.
Comment empêcher Claude Code d’écrire un secret inline ?
Utiliser :
CLAUDE.md
+
PreToolUse hook
L’un communique la règle.
L’autre l’impose.
Piège d’examen
Un scénario peut proposer :
Un API key utilisé par un MCP server ne fonctionne plus dans un CI runner. Le credential est stocké dans un fichier local du runner. Quelle correction est la plus appropriée ?
La réponse ne doit pas être limitée à :
mettre une nouvelle clé dans le même fichier
Il faut identifier les deux problèmes :
clé rejetée
+
secret stocké au mauvais endroit
La correction cohérente avec le module est donc :
rotation
+
suppression du secret du fichier
+
environment variable dans le CI runner
+
configuration MCP par référence
Conclusion
La gestion d’un secret ne se résume pas à « cacher une clé ».
Elle doit couvrir tout son cycle de vie.
Le modèle proposé dans le module est :
SEPARATION
↓
le secret ne voyage pas avec la config
STORAGE
↓
la valeur vit dans un emplacement approprié
ROTATION
↓
la valeur peut être remplacée proprement
Puis il faut limiter les conséquences d’un incident :
least privilege
+
nombre limité de consommateurs
+
inventaire
+
contrôles déterministes
Une intégration MCP devient réellement robuste lorsque le credential peut être utilisé sans être exposé, remplacé sans modifier le code et compromis sans ouvrir un accès plus large que nécessaire.
Dans la suite de la série
OAuth et MCP : réussir le passage du staging à la production
Nous verrons pourquoi une intégration OAuth peut fonctionner parfaitement en staging puis échouer immédiatement en production, comment fonctionnent les redirect URIs et pourquoi certains environnements imposent des OAuth app registrations distinctes.

