Aller au contenu
English
Compte : non connecté, synchronisation possible Mon apprentissage
Selectionner le thème
English
Read in English

Le problème du secret zéro : qui donne le premier secret ?

Par mis à jour le

Améliorer cette page

Le problème du secret zéro décrit une boucle : pour lire ses mots de passe dans un coffre à secrets, une application doit d'abord s'authentifier, donc détenir un premier secret. Ce secret-là doit être protégé à son tour, et le ranger dans un second coffre en exige un autre. La réponse moderne casse la boucle : on ne donne plus un secret à l'application, on lui donne une identité prouvable, attestée par la plateforme qui la fait tourner.

Cette leçon s'adresse au DevOps qui sort ses secrets du code et bute sur cette question. Trois mesures l'illustrent : un jeton de jonction SPIRE 1.15.3 est refusé dès sa seconde utilisation, un jeton de ServiceAccount Kubernetes est invalidé à la suppression du pod, et un SecretID AppRole emballé par Vault 2.1.1 ne se déballe qu'une fois.

Le mot secret désigne ici toute donnée sensible qu'une application utilise pour se connecter à un service : un mot de passe de base de données, une clé d'API, un jeton. Concrètement, vous saurez :

  • Décrire le problème du secret zéro et la boucle récursive qu'il crée
  • Distinguer les mauvaises réponses (secret en dur, variable d'environnement) des bonnes
  • Expliquer comment l'identité de charge et l'attestation brisent la boucle
  • Réduire le risque quand un secret d'amorçage reste inévitable : usage unique, durée courte, emballage
  • Définir les termes du domaine : attestation, OIDC, SPIFFE, SVID
  • Rattacher ce choix à l'exigence SOCLE-SEC-GEN-5, qui privilégie les identités courtes (OIDC)

Le problème du secret zéro (en anglais secret zero ou bootstrap trust problem, le problème d'amorçage de la confiance) part d'un constat simple. Les bonnes pratiques interdisent d'écrire un mot de passe dans le code : on range les secrets dans un coffre à secrets, un logiciel qui les garde chiffrés et ne les délivre qu'aux demandeurs autorisés. L'application interroge le coffre au démarrage.

Une question surgit aussitôt : comment l'application prouve-t-elle au coffre qu'elle a le droit d'accéder aux secrets ? Le coffre ne délivre rien à un inconnu. Il faut donc lui présenter un premier jeton ou un premier mot de passe : c'est le secret zéro. On a déplacé le problème sans le résoudre, il reste un secret à placer quelque part.

La difficulté se répète à l'infini, comme le montre la partie basse du schéma. Si le secret zéro est en clair dans le code, on retrouve le problème de départ. S'il est rangé dans un second coffre, ce coffre exige lui aussi un secret pour s'ouvrir, un secret zéro numéro deux. Chaque couche ajoutée déplace le premier secret, aucune ne le supprime.

Le secret zéro ouvre le coffre à secrets, mais le protéger dans un second coffre exige un autre secret, d'où une boucle. La voie du haut casse la boucle : la plateforme atteste une identité (jeton OIDC, SVID), présentée au coffre, qui délivre des secrets courts à l'application.ouvreà protéger dansexige un autreattesteprésentée àsecrets courtsApplicationau démarrageSecret zéro(jeton, mot de passe)Coffre à secretsSecond coffre ?Plateforme(cloud, Kubernetes)Identité attestée(jeton OIDC, SVID)
Source · SVG
Échap pour fermer, + et − pour zoomer, défilement pour se déplacer. 100 %

La partie haute du schéma, en rouge, montre la sortie : la plateforme (cloud, Kubernetes) atteste une identité que le coffre vérifie, et le coffre délivre des secrets courts à l'application. Il n'y a plus de première clé figée à cacher, seulement une preuve que la plateforme renouvelle.

Face à cette boucle, il existe des réponses commodes mais dangereuses, et des réponses solides qui la brisent. La différence tient à une idée : arrêter de transmettre un secret, et commencer à prouver une identité.

La première mauvaise réponse est le secret en dur (hardcoded secret), écrit directement dans le code source. Le secret zéro se retrouve dans un fichier versionné, souvent poussé sur un dépôt Git, parfois public. Une fois dans l'historique, il y reste même après suppression du fichier, tant qu'on ne réécrit pas l'historique, et surtout tant qu'on ne le révoque pas.

La deuxième est la variable d'environnement, une valeur injectée dans l'environnement du processus au lancement. Le secret ne vit plus dans le dépôt, mais il faut bien le définir quelque part : un script de déploiement, un fichier de configuration, un orchestrateur. Et les variables d'environnement fuient facilement : journaux, rapports d'erreur, outils de diagnostic qui listent l'environnement d'un processus.

Le point commun de ces deux approches : le secret zéro reste un secret statique, une valeur fixe qu'il faut poser quelque part et qui reste valide longtemps. La leçon sur les secrets statiques détaille pourquoi ce modèle accumule le risque.

La bonne réponse : l'identité de charge par attestation

Section intitulée « La bonne réponse : l'identité de charge par attestation »

La réponse moderne renverse le raisonnement. Plutôt qu'un secret, on donne à l'application une identité de charge (workload identity : l'identité propre d'un programme qui tourne, distincte de celle d'un humain). L'application ne dit plus « voici mon mot de passe », elle dit « voici qui je suis, vérifiez-le auprès de la plateforme qui me fait tourner ».

Le mécanisme qui rend cela possible est l'attestation : faire confirmer un fait par un tiers de confiance. La plateforme d'exécution (le cloud, Kubernetes) sait quelle application elle fait tourner, et peut témoigner de son identité auprès du coffre. Le coffre ne fait plus confiance à un secret présenté par l'application, il fait confiance à la signature de la plateforme.

Deux standards structurent ce domaine. OIDC (OpenID Connect) est un protocole d'authentification très répandu : la plateforme délivre un jeton OIDC signé, de courte durée, que le coffre vérifie. Ce jeton n'est pas un secret à cacher durablement : il expire vite et porte une audience précise, donc il ne vaut rien ailleurs ni plus tard.

Le second est SPIFFE (Secure Production Identity Framework For Everyone), un cadre ouvert qui donne à chaque charge de travail une identité vérifiable, indépendante du fournisseur cloud. Cette identité prend la forme d'un SVID (SPIFFE Verifiable Identity Document), délivré et renouvelé automatiquement. Mesuré sur SPIRE 1.15.3, l'implémentation de référence : un X509-SVID vaut 1 heure et un JWT-SVID 5 minutes par défaut.

L'attestation ne supprime pas toute preuve initiale, elle la rend courte, liée à un contexte et souvent à usage unique. Les deux mesures suivantes montrent ce que cela change concrètement par rapport à un mot de passe posé dans un fichier.

Pour enregistrer un nœud sans preuve matérielle, SPIRE propose un join token : un jeton généré par le serveur, ici avec une durée de 600 secondes. L'agent l'utilise pour son attestation de nœud, puis le serveur l'invalide. Un second agent qui rejoue le même jeton est refusé.

$ spire-server token generate -spiffeID spiffe://example.org/node/verif -ttl 600
Token: f5c790ed-beb5-4a48-af8e-3d407d42629c
# Premier agent
level=info msg="Node attestation was successful" ...
# Second agent, même jeton
level=error msg="Agent crashed" error="... failed to attest:
join token does not exist or has already been used"

Le join token reste un secret zéro, mais un secret zéro jetable : volé après usage, il ne vaut plus rien. En production, SPIRE préfère une attestation par la plateforme (document d'identité d'instance cloud, TPM, jeton de ServiceAccount Kubernetes), qui ne demande aucun secret posé à la main.

Dans Kubernetes, l'identité d'un pod est un JWT signé par l'API du cluster, projeté dans le conteneur. Mesuré sur Kubernetes 1.37 : un jeton projeté avec expirationSeconds: 3600 vaut une heure, porte le nom du pod et son UID, et devient invalide dès que le pod est supprimé.

# Revue du jeton (TokenReview) après suppression du pod
"error": "[invalid bearer token, service account token has been invalidated]"

Un attaquant qui copie ce jeton n'obtient pas une clé permanente : il obtient une preuve qui expire dans l'heure et meurt avec le pod. La leçon sur l'identité machine montre comment un cloud ou un coffre consomme ce jeton.

Certains environnements n'offrent aucune attestation : un serveur physique sans TPM, une plateforme qui ne sait pas émettre de jeton OIDC. Il faut alors livrer un secret d'amorçage, mais on peut le rendre inexploitable une fois volé. C'est l'introduction sécurisée (secure introduction), que Vault implémente avec la méthode AppRole.

AppRole sépare l'identifiant en deux moitiés : un RoleID, peu sensible, posé dans l'image, et un SecretID, livré au dernier moment. Le SecretID peut être limité à un usage et à une durée, et livré emballé (response wrapping) : le coffre renvoie un jeton d'emballage à usage unique qui ne contient que l'adresse du vrai secret. Mesuré sur Vault 2.1.1 :

$ vault write auth/approle/role/app secret_id_ttl=10m secret_id_num_uses=1 \
token_ttl=15m token_max_ttl=1h token_policies=default
$ vault write -wrap-ttl=60s -f auth/approle/role/app/secret-id
wrapping_token_ttl: 1m
# Premier déballage : le SecretID est récupéré
# Second déballage (un attaquant qui a intercepté le jeton) :
* wrapping token is not valid or does not exist
# Connexion avec RoleID + SecretID : jeton Vault de 15 minutes
token_duration 15m
# Seconde connexion avec le même SecretID (num_uses=1) :
* invalid role or secret ID

L'intérêt est double. L'interception est inutile après coup, et elle est détectable : si l'application légitime échoue à déballer, quelqu'un l'a fait avant elle, et l'alerte doit partir. Le secret d'amorçage n'est plus un mot de passe éternel, c'est un ticket à usage unique valable une minute.

Ce concept se met en place avec des outils concrets. La leçon sur l'identité machine et workload identity détaille comment une application obtient une identité vérifiable, par SPIFFE/SPIRE pour les échanges internes ou par la fédération OIDC des clouds, puis s'en sert pour s'authentifier sans manipuler de secret zéro.

Côté coffre, le guide HashiCorp Vault montre comment un coffre accepte une identité attestée plutôt qu'un mot de passe fixe : méthode d'authentification Kubernetes, JWT/OIDC, ou AppRole quand aucune attestation n'existe. Combiné à l'identité de charge, il ferme la boucle de bout en bout.

Enfin, le moindre privilège reste indispensable : une identité, même solidement attestée, ne doit ouvrir que les secrets nécessaires à l'application. Une identité fiable qui ouvre tout le coffre reste un risque, simplement mieux tracé.

Ces quatre pièges reviennent dans les premiers déploiements. Le premier est le plus fréquent : il donne une impression de sécurité alors que le secret zéro est toujours là, simplement déplacé dans une autre configuration.

Le premier piège est de croire le problème résolu par un coffre seul. Installer un coffre est utile, mais si l'application y accède avec un mot de passe fixe stocké dans sa configuration, le secret zéro est caché, pas éliminé. Le coffre centralise les secrets, il ne règle pas l'amorçage.

Le deuxième est de traiter un jeton éphémère comme un secret durable. Un jeton OIDC ou un SVID tire sa sécurité de sa courte durée de vie et de son renouvellement automatique. Le recopier dans un fichier ou le figer dans une variable recrée exactement le secret statique qu'on voulait supprimer.

Le troisième est de négliger la racine de confiance. L'identité de charge repose sur la plateforme qui atteste. Si n'importe quel conteneur peut réclamer n'importe quelle identité, ou si les entrées d'enregistrement SPIRE sont trop larges, l'attestation ne vaut rien. La sécurité se concentre sur cette racine, qui devient le nouveau point critique.

Le dernier est d'oublier la révocation et l'audit. Passer à l'identité de charge ne dispense pas de savoir qui a accédé à quoi, ni de pouvoir couper l'accès d'une charge compromise. Une identité vérifiable mal tracée reste un angle mort en cas d'incident.

  • Le secret zéro est le premier secret, celui qui permet à une application d'ouvrir le coffre où sont rangés les autres.
  • Le problème est récursif : le protéger dans un autre coffre crée un nouveau secret zéro.
  • Le secret en dur et la variable d'environnement déplacent le secret statique sans casser la boucle.
  • L'attestation renverse le problème : la plateforme prouve l'identité de l'application, qui ne présente plus de secret.
  • OIDC et SPIFFE fournissent des identités éphémères : mesuré sur SPIRE 1.15.3, 1 heure pour un X509-SVID, 5 minutes pour un JWT-SVID.
  • Quand un secret d'amorçage reste inévitable, on le rend à usage unique : join token SPIRE, SecretID AppRole emballé, refusés à la seconde utilisation.
  • La racine de confiance devient le point critique : la plateforme qui atteste doit être protégée.
  • La preuve attendue par SOCLE-SEC-GEN-5 est l'usage d'identités courtes ou éphémères (OIDC) plutôt que de secrets de longue durée.

Vérifiez que l'essentiel de cette leçon est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Ce projet vous est utile ?

Les guides, formations, quiz et labs restent gratuits. Si vous les utilisez régulièrement, vous pouvez contribuer à leur maintenance et à leur développement.

Devenir membre de soutien (nouvel onglet) Soutenir une fois (nouvel onglet) En savoir plus

Suivez aussi le projet sur LinkedIn