Image layers are scanned for hardcoded credentials and secrets
Why it matters
Secrets baked into Docker image layers are permanently readable by anyone with registry pull access — including future employees, CI systems, and compromised tokens (OWASP A02, CWE-540). Docker layer history exposes every ARG, ENV, and RUN instruction even after a subsequent RUN unset — the value persists in the lower layer. NIST 800-53 SC-28 requires protection of information at rest; credentials in registry layers violate that control and are disclosed with every docker history command. This is one of the most common paths to credential exposure in containerized environments.
Severity rationale
Critical because credentials baked into image layers are exfiltrated by anyone with registry read access — no runtime exploit required.
Remediation
Use multi-stage builds to ensure secrets never land in the final image layer. In Dockerfile:
# Build stage — secret is accessible here
FROM node:20 AS builder
ARG PRIVATE_TOKEN
RUN git clone https://${PRIVATE_TOKEN}@github.com/org/private-repo.git /src
RUN cd /src && npm ci && npm run build
# Runtime stage — secret never copied
FROM node:20-alpine
COPY --from=builder /src/dist /app
CMD ["node", "/app/index.js"]
For runtime secrets, inject via Kubernetes Secrets (secretKeyRef) or a secrets manager (Vault, AWS Secrets Manager). Never use ENV SECRET=value in the final stage — use docker secret or runtime injection instead.
Detection
-
ID:
no-secrets-in-layers -
Severity:
critical -
What to look for: Enumerate every RUN, COPY, ADD, ARG, and ENV instruction across all Dockerfiles. For each instruction, classify whether it handles sensitive data (API keys, passwords, private keys, tokens, database credentials). Count the total instructions that reference or embed secrets.
-
Pass criteria: No Dockerfile instruction contains hardcoded secrets, API keys, passwords, or private keys — 0 of the total instructions reference embedded credentials. All sensitive data is injected at runtime via environment variables, Kubernetes Secrets, or a secrets manager rather than baked into image layers. Multi-stage builds are used to exclude at least 100% of build-time secrets from the final image. Report: "Scanned X instructions across Y Dockerfiles, 0 secrets found in layers."
-
Fail criteria: Any hardcoded secret, API key, password, private key, or database credentials found in Dockerfile layers. Secrets visible in
.dockerignorebut not excluded, or stored in image layers. -
Skip (N/A) when: The project does not have a Dockerfile.
-
Detail on fail: Quote the offending instruction. Example:
"Dockerfile RUN command contains git clone with hardcoded PAT: 'git clone https://token:abc123@github.com/...'"or"AWS credentials passed as ARG without being excluded from final layer" -
Remediation: Never hardcode secrets in Dockerfiles. Use multi-stage builds to exclude secrets from the final image:
# Build stage with secrets FROM node:20 AS builder ARG PRIVATE_TOKEN RUN git clone https://${PRIVATE_TOKEN}@github.com/org/repo.git # Final stage — no secrets FROM node:20 COPY --from=builder /app /app # PRIVATE_TOKEN is not in this stageAt runtime, inject secrets via Kubernetes Secrets or environment variables — never bake them into the image.
External references
- cwe · CWE-540 — Inclusion of Sensitive Information in Source Code
- owasp:2021 · A02 — Cryptographic Failures
- nist:rev5 · SC-28 — Protection of Information at Rest
- external · CIS-Docker-4.10 — CIS Docker Benchmark v1.6 §4.10 — Do not store secrets in Dockerfiles
Taxons
History
- 2026-04-18·v1.0.0·Initial import from infrastructure-hardening·automated