On 10 December 2025 the security firm Flare published a scan of Docker Hub images uploaded in November 2025. It found 10,456 container images with one or more exposed keys. After filtering to high and critical findings, 205 namespaces remained, and 101 of them were tied to identifiable companies, including one Fortune 500 firm.
The report says the cause is mundane. Developers keep credentials in a local .env file. If the whole project directory is copied into a Docker build, that file ends up inside the image for anyone who pulls it. Libraries such as dotenv and python-dotenv make the file convenient, and a careless COPY . . makes it public.
What's affected
From Flare's numbers:
- About 42% of the leaking images held five or more secrets.
- Almost 4,000 AI provider keys were exposed.
- Named examples included GitHub, npm, PyPI and AWS credentials, plus keys for OpenAI, Anthropic, Hugging Face, Gemini and Groq.
- About 25% of developers removed the leaked secret within one to two days, but the report says credentials were often not revoked. It states that 75% of exposed organizations failed to revoke or rotate their keys.
- Personal and contractor registries were part of the picture. One freelancer account had 70 repositories, and a bank's chief architect had over 430 public images in a registry.
This is not only a public registry problem. A private registry image is one stolen pull credential away from the same exposure.
How to check if you're exposed
Inspect an image the way a stranger would. These commands work on any image you can pull.
# Is an .env or key file in the filesystem?
docker create --name probe myorg/app:latest
docker cp probe:/app/.env ./probe.env && echo "FOUND .env"
docker rm probe
# Do build instructions carry values?
docker history --no-trunc myorg/app:latest | grep -iE 'token|secret|password|key'
Then check the repository side for the patterns that cause it:
grep -nE '^(COPY|ADD) +\. ' Dockerfile
test -f .dockerignore && grep -nE '\.env|\.pem|\.aws' .dockerignore || echo "no .dockerignore rules"
For a wider pass, run a scan across your images and your repositories. The free Safeguard dependency scan checks package manifests in the browser, but it will not find files baked into layers, so use a secret scanner for that.
What to do now
- Treat any key found in an image as compromised. Revoke or rotate it first. Flare's data shows removing it from the image is not enough.
- Add a
.dockerignorebefore anything else:
.env
.env.*
*.pem
*.key
.aws
.git
- Stop copying the whole directory. Copy named paths instead of
COPY . .. - Do not pass secrets with
ARGorENV. Docker's documentation says build arguments and environment variables persist in the final image. Use a secret mount, which exists only for the duration of the instruction that uses it:
RUN --mount=type=secret,id=npm_token,env=NPM_TOKEN npm ci
docker build --secret id=npm_token,env=NPM_TOKEN .
- Inject runtime secrets when the container starts, from your orchestrator or a secrets manager, not at build time.
- Prefer short-lived credentials. For cloud access from CI, use OIDC federation instead of storing a key, as in our GitHub OIDC guide.
- Scan images in CI before pushing, and scan registries you do not control, including personal and contractor accounts.
How to avoid this trap
- Ignore files by default. A
.dockerignorewith.env.*removes the most common cause Flare names. - Build secrets are not image content.
--mount=type=secretis built for this and leaves nothing in a layer. - Rotate on exposure, always. The 75% figure shows the real failure is the step after the cleanup.
- Own your registries. Contractor accounts sit outside corporate oversight. Put image publishing behind your organization.
- One image, one purpose. Keep local config out of the project root that the build sees.
Try it yourself
Take your most recent production image, run the three inspection commands above, and write down what you find. Then scan it.
Click through it at your own pace, or open it full screen.
Quick check
Why are ARG and ENV the wrong place for a build secret?
Docker's documentation says build arguments and environment variables persist in the final image, so anyone with the image can read them.
You deleted the .env file and pushed a new image tag. Are you done?
No. The old image and its layers may already be copied, and the key must be revoked or rotated.