BuildNot yet confirmed elsewhere1 publisher3 min readPublished
A Docker Hub account conversion let one user's access token act as another user
Docker says it fixed a Docker Hub token flaw about four hours after an outside researcher reported it on September 30. Teams that publish from CI should check whether Docker flagged their tokens and whether their published images still match their builds.
The Engineer · Build desk
Key figures: 42 images verified, and each traced back to the CI run that built it. Docker planned to deactivate 2 CI tokens, which were replaced by 3 scoped tokens. Docker said it fixed the flaw within about 4 hours.
| Figure | Value | Note | Claim |
|---|---|---|---|
| Images verified and traced to their CI run | 42 images | 12 | |
| CI tokens to deactivate | 2 tokens | 4 | |
| Scoped replacement tokens | 3 tokens | 19 | |
| Docker's fix time, about | 4 hours | 21 |
What happened
- Docker emailed the compose-lint maintainer twice on October 2, an hour apart; only the copy sent to the user account was titled "Action Requested".
- The notice said Docker planned to force-deactivate the maintainer's two CI tokens on Monday, October 5.
- The maintainer ran cosign verify on every tag against a pinned workflow identity, and all 42 images passed.
Why it matters
- exposure Had the flaw been exploited, pullers of a flagged publisher's tags could have received an image the publisher never built, so the publisher's audit is the evidence they depend on.
- constraint The signature route is open only to teams that already sign at build time with a pinned identity. Everyone else is left with push timestamps reported by the registry whose access control failed.
- decision Teams adding signing now choose between one publishing workflow and an identity pattern that must list every workflow that ever republished a release.
- cost Rotation left the maintainer with three scoped tokens where Docker flagged two, which is one more secret to replace the next time a notice arrives.
Docker's side of this reaches us through one person. tmatens, who maintains the compose-lint linter, wrote up the notices on dev.to, and Docker's statement that it had seen no sign of exploitation is as tmatens relays it [22].
The two copies of the notice carried different weight. tmatens skimmed the one addressed to the organization owner, which said there was no action to take, and moved on. On a closer read, the user account was the one flagged [5]. The post describes the October 5 deactivation as planned and does not report whether it ran [4].
The check works because of where the signature comes from. Every compose-lint image is signed with cosign in keyless mode, using GitHub Actions' OIDC identity, and the certificate records the workflow and tag that produced the image, such as publish.yml@refs/tags/v0.34.0 [6]. Anyone holding Docker Hub push rights could still push an image. According to tmatens, that person could not produce the matching signature without running the workflow in the repo on a release tag [7]. tmatens wrote: "That meant I didn't have to take Docker's word for it." [8]
A valid signature alone proves little, since anyone can sign an image [9]. The verify command pins both ends. `--certificate-identity-regexp` matches only the publish and publish-channel workflows on a vX.Y.Z tag in tmatens/compose-lint, and `--certificate-oidc-issuer` is set to https://token.actions.githubusercontent.com [10]. The regexp lists two workflows because release 0.3.4 was republished by a different one [11]. All 42 images passed [12].
The loop fetches tags with one request at page_size=100 [18]. That covers 42 tags. It would not reach tags past the first page of a larger repo [20].
Signatures answer a narrow question: what does the tag point to now. A tag moved to a bad image and moved back would verify afterward [13]. So tmatens also checked each tag's last_pushed time, taken from the Docker Hub API, against the release runs. Every push lined up, and the tag list matched the releases with nothing extra and nothing missing [14]. A push from outside the release workflow would leave a timestamp with no run behind it, and moving a tag back is a push too.
The audit also turned up a hole in the pipeline. Right after pushing, the release workflow signed whatever the tag referenced. During the seconds between the push and the signature, anyone with write access to the registry could have repointed the tag and gotten the workflow to sign their image [16]. A signature from that workflow would then have vouched for an image it never built. The workflow now confirms that the tag resolves to exactly the image the build pushed, and halts before signing on a mismatch [17].
Then came the rotation. tmatens replaced the flagged tokens and split them so each job holds only the one it needs: read-only for nightly scans, push for releases, and a separate one for the Docker Hub description [15].
What to watch
- A Docker advisory that spells out the conditions around account-to-organization conversion; the account here is one maintainer's summary of the notice.
- Confirmation from Docker that the October 5 deactivation ran, and how many tokens it covered.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence50
- Adoption
- Insufficient
- Hype gap
- Insufficient
- Incentives25
- Confidence55
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
Under certain conditions around converting a personal account to an organization, one Docker Hub user could take on another user's capabilities through a personal access token, which means pushing or pulling images as them.
- [2]
An outside researcher reported the flaw in Docker Hub's identity and access management on September 30.
- [3]
tmatens received two emails on Friday, October 2, about an hour apart: one addressed to them as organization owner said there was no action to take, and one sent to their user account was titled "Action Requested".
- [4]
Docker planned to force-deactivate tmatens's two CI tokens on Monday, October 5.
- [5]
tmatens skimmed the organization-owner email and moved on; on a closer read of both, the user account was the one flagged.
- [6]
Every compose-lint image is signed with cosign, keyless, using GitHub Actions' OIDC identity; the signing certificate records the workflow and tag that produced the image, such as publish.yml@refs/tags/v0.34.0.
- [7]
Someone with Docker Hub push access could push an image but could not produce the signature without running the maintainer's workflow in the repo on a release tag.
- [8]
"That meant I didn't have to take Docker's word for it."
- [9]
Checking that an image has a valid signature is not enough on its own, since anyone can sign an image; you have to pin who signed it.
- [10]
The cosign verify command uses --certificate-identity-regexp to match the publish and publish-channel workflows in tmatens/compose-lint on vX.Y.Z tag refs, and --certificate-oidc-issuer https://token.actions.githubusercontent.com.
- [11]
Release 0.3.4 was signed by a different workflow used to republish it, so verifying everything required a regexp allowing both workflows instead of one exact identity.
- [12]
All 42 images verified, and every one traced back to the CI run that built it.
- [13]
A signature check only tells you what a tag points to today; someone could point a tag at a bad image, wait for pulls, then point it back, and everything would still verify afterward.
- [14]
tmatens pulled each tag's last_pushed time from the Docker Hub API and matched it against release runs; every push lined up with a real run, and the tag list matched the releases with nothing extra and nothing missing.
- [15]
tmatens rotated the tokens and split them so each job gets only the one it needs: read-only for nightly scans, push for releases, and a separate one for updating the Docker Hub description.
- [16]
The release workflow signed whatever the tag pointed to right after pushing it; in the few seconds between push and sign, someone with write access to the registry could have moved the tag and gotten their image signed by the workflow.
- [17]
The workflow now checks that the tag points to exactly what the build pushed and stops before signing.
ReportedSupportedSource: tmatens, dev.to (post text is cut off mid-word at 'signin')View cited source - [18]
The verification loop lists tags with a single request to the Docker Hub API using page_size=100.
- [19]
The two flagged CI tokens were replaced by three scoped tokens, one more than before.
- [20]
A single request at page_size=100 covers 42 tags but would not reach tags beyond the first 100 in a larger repository.
- [21]
Docker said it fixed the flaw within about four hours.
- [22]
Docker said it had not seen any sign that the flaw was exploited.
Sources
1 independent publisher whose own reporting we read for this story.
- dev.toDocker Hub's access token vulnerability: how signing let me verify my images
1 article · October 12, 2026
Topics and entities
Follow any of these and your For You feed starts watching them — no settings page required.
Topics
- Software Supply Chain SecurityFollow
- CI Credential ManagementFollow
- Docker Hub securityFollow
- Container Image SigningFollow