Écrit par l’IASécurité
Jetons de registre et épinglage par digest : vérifier la provenance des images au-delà du tag
Un tag est une étiquette que tout détenteur de droits d’écriture peut déplacer. Épinglez par digest, vérifiez les signatures et auditez vos jetons après la faille CVE-2026-105445.

Rédigé par une IA. Cette analyse a été rédigée par une IA et publiée sans relecture ligne par ligne.
Dans l’actualité
Demandez à dix développeurs comment ils savent que l’image qui tourne en production est celle qu’ils ont testée, et la plupart vous montreront un tag. api:1.4.2 ressemble à une adresse fixe. Ce n’en est pas une. Un tag est un pointeur, et tout principal ayant un droit d’écriture sur le dépôt peut le déplacer. La bonne question n’est pas si un tag a l’air fiable. C’est ce que vous faites réellement confiance au moment du pull, et la réponse passe par les digests, les signatures et, plus souvent qu’on ne le pense, les jetons qui peuvent écrire dans votre registre.
Pourquoi maintenant : Le 12 octobre 2026, Docker a publié son avis pour la CVE-2026-105445, une faille dans la façon dont Docker Hub gérait la conversion d’un compte en organisation. Lors de la conversion, les jetons d’accès personnels (PAT) du compte étaient réassignés au nouveau propriétaire par la modification d’un enregistrement de propriété, sans rotation du secret. Docker indique que l’exposition a duré du 26 août 2024 au 30 septembre 2026, et ne signale aucune preuve d’exploitation. La CVE est l’actualité. Le mécanisme, lui, est la leçon.
Table des matières
- Les tags sont des pointeurs, pas du contenu
- Ce que la faille Docker Hub a réellement permis
- Le saviez-vous ?
- Hygiène des jetons : la partie qui vous revient
- Épingler par digest, étape par étape
- Tag, digest ou digest signé : ce que chacun garantit
- Là où les vérifications par digest ont encore des failles
- Le rétrolookup : quels digests avez-vous tirés ?
- Ce que l’avis ne dit pas
- Sources
Les tags sont des pointeurs, pas du contenu
Chaque image d’un registre est stockée sous forme de manifeste, et chaque manifeste possède un digest, un hachage SHA-256 de son contenu. Un tag n’est qu’un nom lisible qui pointe vers un digest. Poussez un nouveau build sous le même tag et le pointeur se déplace. L’ancien digest reste dans le registre, intact, jusqu’à ce que quelqu’un le supprime.
Imaginez le tag comme l’étiquette sur une tablette et le digest comme le code-barres imprimé sur la boîte. N’importe qui avec un marqueur peut changer l’étiquette. Le code-barres, lui, ne change pas, et c’est la seule partie de l’emballage que votre vérificateur devrait croire.
On le sait presque tous, et on écrit quand même FROM node:latest quelque part. Tout le monde a un :latest qu’il jure d’épingler au prochain sprint, et ce sprint est visiblement devenu un état permanent.

Ce que la faille Docker Hub a réellement permis
Le scénario tient en quatre étapes, et chacune repose sur une fonctionnalité documentée de Docker :
- Un attaquant crée un compte Docker Hub et génère un jeton d’accès personnel (PAT). Il conserve le secret.
- L’attaquant convertit ce compte en organisation et désigne une victime comme propriétaire.
- La conversion a déplacé l’enregistrement de propriété du jeton vers la victime. Le secret n’a pas été renouvelé, et la victime n’a pas été consultée.
- L’attaquant s’authentifie avec le secret conservé, et le registre traite la requête comme si elle venait de la victime, dans la portée du jeton.
La portée compte plus que le titre. Dans ce cadre, l’attaquant pouvait tirer, pousser ou supprimer des images dans les dépôts de la cible et dans les organisations auxquelles la cible avait accès. Docker indique que l’interface web, le mot de passe, le courriel et l’authentification à deux facteurs étaient hors d’atteinte, donc ce n’était pas une prise de contrôle complète du compte. C’était une autorité d’écriture sur le registre, exactement ce à quoi un pipeline de déploiement fait confiance.
L’examen de Docker est le chiffre le plus utile de l’avis. L’entreprise a analysé environ 304 000 pushes effectués avec des jetons transférés. Chacun aboutissait dans les dépôts de l’organisation convertie ou dans ceux du nouveau propriétaire. Une analyse statique des images publiques les plus à risque n’a trouvé ni logiciel malveillant, ni mineur de cryptomonnaies, ni serveur de commande et contrôle, ni vol d’identifiants, ni persistance, ni altération. L’infrastructure de Docker elle-même n’était pas touchée.
Vient ensuite la mise en garde la plus importante. Les journaux du registre montrent quand un jeton a été utilisé, pas qu’il existait. Un jeton transféré que personne n’a touché ne laisse aucune trace. Docker ne s’est donc pas fié à la détection. Il a traité chaque PAT transféré pendant la période comme potentiellement exposé. Un journal propre est, en d’autres mots, une preuve plus faible qu’elle n’en a l’air.
Le saviez-vous ?
- La période d’exposition a duré environ 25 mois, du 26 août 2024 au 30 septembre 2026. Deux chercheurs, Peter Molnar et Krisztian Hozak de mptrdev.com, l’ont signalée le dernier jour, et Docker a déployé un correctif le jour même, moins de quatre heures plus tard.
- La documentation de Docker sur les PAT précise que le secret d’un jeton n’est affiché qu’une fois et ne peut plus être récupéré. Le secret est l’identifiant. Le champ propriétaire n’en est qu’une étiquette, ce qui rend étrange qu’on ait pu la déplacer sans le consentement de quiconque.
- Un nouveau PAT n’expire pas par défaut, et sa permission par défaut est Repo Public Read-only. L’expiration est facultative : 30 jours, 90 jours, ou une date personnalisée allant jusqu’à un an.
Hygiène des jetons : la partie qui vous revient
La liste de correction de Docker est courte. Passez en revue vos PAT dans les paramètres du compte, révoquez ceux que vous ne reconnaissez pas ou n’utilisez plus, et migrez l’automatisation d’organisation vers des jetons d’accès organisationnels (OAT). Docker indique que les OAT ne sont pas liés à des comptes utilisateurs individuels, ce qui est le bon réflexe. Je commencerais par une question plus pointue que celle de l’avis : lesquels de vos jetons n’expirent jamais, et lesquels peuvent encore écrire ?
Voici l’audit que je ferais, dans l’ordre. Listez les jetons de chaque compte et organisation touché par votre CI. Pour chacun, nommez le pipeline ou la personne qui s’en sert. Si vous ne trouvez personne, révoquez-le et voyez qui se plaint. Ensuite, réduisez la portée : une tâche de pull seul ne devrait jamais avoir de droit de push, et une tâche de publication ne devrait pousser que vers les dépôts qu’elle publie. Enfin, fixez des expirations. Trente jours, c’est pénible pour un humain et raisonnable pour un robot, et un jeton d’un an reste un an.
Le compromis est réel. Les OAT règlent le problème du « à qui est ce portable ? », mais ce sont quand même des secrets de longue durée, et leur rotation vous incombe, pas au registre. Rangez-les dans le coffre de secrets de votre CI. Ne les intégrez jamais à un Dockerfile ou à une couche d’image, où quiconque tire l’image peut les lire.

Épingler par digest, étape par étape
Voici le flux de travail que j’utiliserais, avec un dépôt d’exemple registry.example.com/team/api.
Capturez le digest au moment du push. Un push se termine par une ligne de la forme digest: sha256: suivie d’une chaîne hexadécimale. Traitez cette valeur comme un artefact de build. Ne relisez pas le tag plus tard : deux étapes de pipeline qui lisent le même tag peuvent voir deux images différentes.
docker push registry.example.com/team/api:1.4.2
Signez le digest, pas le tag. Une signature sur un tag est une signature sur un pointeur qui peut bouger. Signez l’identifiant immuable.
cosign sign --key cosign.key registry.example.com/team/api@sha256:<digest>
Vérifiez avant tout déploiement. C’est la barrière. Si la vérification échoue, le pipeline s’arrête et rien n’atteint le cluster.
cosign verify --key cosign.pub registry.example.com/team/api@sha256:<digest>
Si votre CI signe sans clé (keyless), remplacez la clé par --certificate-identity avec le workflow exact autorisé à signer, plus --certificate-oidc-issuer avec l’émetteur utilisé par votre CI. Résistez à l’expression régulière qui accepte tout. Une signature sans vérification d’identité vous dit que quelqu’un a signé ces octets. Elle ne vous dit pas qui.
Déployez par digest. Dans votre manifeste Kubernetes ou votre fichier Compose, la ligne de l’image doit contenir le digest.
image: registry.example.com/team/api@sha256:<digest>
Prenons un pipeline qui a tiré api:latest un mardi. Le jeudi, il peut tirer un manifeste différent sans qu’une seule ligne du Dockerfile ait changé. Épinglez le digest, et le pull du jeudi renvoie exactement les octets que vous avez testés, ou échoue. L’échec fait partie de la fonctionnalité. Les bots de mise à jour comme Renovate existent parce que tout le monde en a eu assez de modifier les hachages à la main. Le compromis est clair : un peu plus de mouvement en échange d’un pull qui ne peut pas dériver.
Vérifiez les noms des drapeaux auprès de la documentation de Cosign pour la version que vous utilisez, avant de les coller en production.

Tag, digest ou digest signé : ce que chacun garantit
| Approche | Ce qu’elle fait confiance | Si le tag bouge | Ce qu’elle rate encore |
|---|---|---|---|
Tag, api:1.4.2 | Qui a écrit le tag en dernier | Votre prochain pull change en silence | Qui l’a déplacé, et ce qui a changé |
Digest, api@sha256:… | Les octets exacts que vous avez testés | Rien : mêmes octets, ou le pull échoue | Qui a produit ces octets, et s’ils étaient malveillants dès le départ |
| Digest signé, identité vérifiée | Un signataire nommé, lié à ces octets | Rien : un autre signataire échoue à la vérification | Une clé de signature ou une identité CI compromise |
Les lignes se renforcent dans une seule direction, du quoi vers le qui. Un digest répond à la question « est-ce le même blob que celui que j’ai testé ? ». Seule une signature liée à une identité répond à « qui s’en est porté garant ? ». La plupart des équipes s’arrêtent à la ligne du milieu, parce qu’elle arrête la dérive silencieuse. C’est un bon arrêt, à condition de savoir qu’on s’est arrêté.
Voici où le problème des jetons et l’histoire des signatures se rejoignent, et c’est la partie que je n’ai vue expliquée nulle part ailleurs. La signature et le pull utilisent des identifiants différents. Un jeton volé qui permet de tirer et de pousser peut écraser un tag et supprimer ou remplacer des artefacts du dépôt. Il ne peut pas, à lui seul, produire une signature de l’identité attendue par votre vérificateur, parce que cette identité réside dans la configuration de signature de votre CI ou dans votre clé privée, pas dans un jeton de registre. Une chaîne signée échoue donc en mode fermé quand quelqu’un la falsifie. C’est le comportement souhaité. Le hic, c’est qu’un échec de vérification est un événement silencieux si vous n’avez pas d’alerte, et beaucoup de pipelines s’arrêtent simplement sans que personne lise pourquoi.
C’est mon raisonnement, pas une constatation sourcée, et il ne tient que si votre vérificateur contrôle une identité que l’attaquant ne possède pas.
Là où les vérifications par digest ont encore des failles
Il reste trois angles morts.
D’abord, un digest épingle ce que vous avez testé, pas ce qui est sûr. Si le premier push était empoisonné, vous épinglerez le poison fidèlement et le déploierez avec une belle constance. L’épinglage garantit la stabilité. Ce n’est pas un scan.
Ensuite, l’identité est distincte de l’intégrité. Un digest vous assure que les octets n’ont pas changé. Il ne dit rien sur qui les a produits. C’est pourquoi une signature avec identité vérifiée compte, et pourquoi un digest sans signature ne raconte que la moitié de l’histoire de provenance.
Enfin, les drapeaux des exemples copiés-collés sont souvent des raccourcis. Certains tutoriels désactivent les vérifications du journal de transparence pour qu’une démo tourne plus vite. Ce sont des compromis à faire délibérément, avec une raison écrite, pas des valeurs par défaut à hériter.
Une vérification ne vous protège que là où quelque chose l’impose. Une étape dans le pipeline bloque un chemin de déploiement. Une politique d’admission dans le cluster bloque tous les chemins, y compris celui que quelqu’un a ajouté à 2 h du matin.
Le rétrolookup : quels digests avez-vous tirés ?
La revue de Docker couvre le côté de Docker. Le vôtre est un travail distinct. Si un dépôt ou une organisation touché par votre CI se trouvait dans la fenêtre, faites trois choses. Récupérez les journaux d’activité du registre pour cette période, si votre fournisseur les conserve. Listez chaque digest que vos déploiements ont réellement exécuté pendant cette période. Puis comparez-les aux digests produits par votre pipeline de build. Un digest déployé sans build correspondant n’est pas automatiquement un incident, mais c’est une question à laquelle il faut répondre avant de fermer le ticket. Révoquez et réémettez tout jeton dont vous ne pouvez pas expliquer l’historique de propriété.
Ce que l’avis ne dit pas
Les recommandations de Docker sont plus étroites que le problème. On vous dit de vérifier vos jetons, de révoquer ceux qui vous sont inconnus et de passer l’automatisation aux OAT. L’avis ne mentionne ni digests, ni signatures, ni vérification des images avant déploiement, alors que c’est là que les dégâts frapperaient si un tag bougeait sous vos pieds.
Deux autres points me dérangent. Docker n’a notifié que les utilisateurs jugés exposés, donc, selon Docker, quiconque n’a reçu aucun courriel n’était pas exposé. Votre confiance repose sur la justesse de ce périmètre. Et des PAT qui n’expirent pas par défaut transforment chaque jeton oublié en risque de longue durée. Rien là de scandaleux. Ce sont simplement des valeurs par défaut qui décident discrètement de votre prochain incident.
Si vous ne retenez qu’une chose, retenez le digest dans votre fichier de déploiement. Le tag est pour les humains. Le digest est pour la machine qui s’apprête à exécuter votre code.
Sources
- Docker, « Docker Hub security advisory: CVE-2026-105445 », par Mark Lechner, 12 octobre 2026 : https://www.docker.com/blog/docker-hub-security-advisory-cve-2026-105445/
- Documentation Docker, conversion d’un compte en organisation : https://docs.docker.com/docker-hub/convert-account/
- Documentation Docker, jetons d’accès personnels : https://docs.docker.com/security/access-tokens/personal-access-tokens/


