
Defending Against Pass-the-Cookie And Pass-the-Hash Attacks In Hybrid Environments
October 20, 2026A single unpatched vulnerability in a base container image can unravel an entire microservices architecture. In 2025, Snyk’s annual State of Open Source Security report found that 82% of container images pulled from public registries contained at least one known critical vulnerability — and the majority of those vulnerabilities lived not in the application code, but in the operating system packages bundled inside the image. That statistic should recalibrate how security architects think about container hygiene.
Docker containers were sold to the industry as isolated, reproducible units of deployment. They are — but isolation is not immunity. A container running on a shared kernel with a bloated image carrying 400 unnecessary packages is not a hardened asset; it’s an attack surface wearing a deployment badge. The discipline of hardening container images through minimal base images and distroless build pipelines is one of the highest-leverage security investments a DevSecOps team can make in 2026.
The Attack Surface Problem: Why Fat Images Are a Security Liability
Most container images start their life as derivatives of a full Linux distribution — Debian, Ubuntu, CentOS, or Fedora. These distributions were designed for general-purpose computing, meaning they ship with shells, package managers, compilers, debugging tools, and thousands of shared libraries that have no place in a production container. Every unnecessary binary is a potential privilege escalation vector. Every extra package is a CVE waiting to be matched against your image digest.
Consider a Node.js application packaged on the default node:18 image. That image, as of mid-2026, carries over 600 installed packages — many of which the application never touches at runtime. Trivy scans routinely identify 150+ vulnerabilities in that single base layer before a single line of application code is added. The application developer wrote clean, audited JavaScript, but the security posture of their container is dominated by apt packages they never chose.
The Principle of Least Functionality Applied to Containers
NIST SP 800-190, the authoritative guide on application container security, explicitly recommends configuring containers to include only the software required for their function. This is least-privilege applied at the filesystem level. When a container has no shell (/bin/sh, /bin/bash), an attacker who achieves code execution through a vulnerability cannot pivot interactively. When there is no package manager, lateral movement through dependency installation becomes dramatically harder. When there are no compilers, memory corruption exploits that require in-memory compilation of shellcode lose a critical capability.
The security model changes fundamentally: instead of trying to detect and block malicious behavior inside a rich environment, you simply remove the tools that enable it.
Minimal Base Images: Alpine, Scratch, and the Tradeoffs
The first practical step toward container hardening is selecting a minimal base image. The spectrum runs from the ultra-minimal scratch image (literally an empty filesystem) to purpose-built minimal distributions like Alpine Linux.
Alpine Linux: Security Benefits and the musl Consideration
Alpine Linux has become the default choice for teams seeking a minimal, general-purpose base. At roughly 5MB, it ships with musl libc and BusyBox instead of glibc and GNU coreutils. The attack surface reduction is real: a vanilla Alpine image carries fewer than 15 installed packages. However, security engineers should be aware that Alpine’s use of musl libc introduces subtle behavioral differences for applications compiled against glibc — particularly around DNS resolution, thread-local storage, and certain C++ ABI behaviors. These are not theoretical issues; Netflix engineering documented a musl-related DNS caching anomaly in their Alpine-based services in 2024 that caused intermittent connection failures in high-throughput environments.
The security tradeoff is clear: Alpine’s minimal footprint significantly reduces the CVE count, but teams must validate application compatibility during staging and cannot assume drop-in compatibility from Debian-based images. For Python and Go applications especially, Alpine has excellent support and is widely recommended by security teams.
FROM Scratch: Maximum Minimalism for Statically Compiled Languages
For languages that compile to static binaries — Go, Rust, and to a lesser extent C/C++ with static linking — the FROM scratch directive produces a container image that is literally just the application binary and any required static assets. No OS, no shell, no libraries. The image size can be under 10MB. A static Go binary in a scratch image exposes zero OS-level CVEs because there is no OS layer to scan.
This approach is production-proven at scale. Cloudflare’s internal container infrastructure extensively uses scratch-based images for their Golang network services, enabling their security team to reduce image scan findings by over 90% compared to their previous Debian-based pipeline. The tradeoff is that debugging becomes harder — kubectl exec into a scratch container drops you into an empty namespace. Teams compensate with ephemeral debug containers (using kubectl debug) attached at investigation time, rather than shipping debug tooling into production images.
Distroless Images: Google’s Approach to OS-Free Production Containers
Google’s distroless project, now maintained under the gcr.io/distroless and cgr.dev/chainguard registry namespaces, represents the most operationally mature solution for teams who need a minimal runtime environment but cannot use scratch due to dynamic library dependencies.
Distroless images contain only the application runtime and its direct dependencies — no shell, no package manager, no init system, no cron. The Java distroless variant includes the JRE but nothing else. The Python variant includes the CPython interpreter and standard library but strips apt, bash, and all administrative tooling. A 2026 benchmark by Chainguard found that their distroless Python image contained zero known CVEs at release time, compared to 87 CVEs in the equivalent python:3.12-slim image — a result achieved through aggressive upstreaming of patches and a daily rebuild cadence.
Implementing Distroless in a Multi-Stage Build Pipeline
The distroless strategy only works efficiently inside a multi-stage Docker build. The pattern is well-established: use a feature-rich builder image in the first stage to compile code, install dependencies, and run tests, then copy only the compiled artifacts into a distroless final stage. The builder stage never reaches production registries; only the hardened final layer does.
A Go application build pipeline illustrates this clearly:
Stage 1 — Builder: FROM golang:1.23 AS builder — Full Go toolchain, module cache, test runner available. Compile and test here.
Stage 2 — Final: FROM gcr.io/distroless/static-debian12 — Copy only the compiled binary. No shell, no package manager. Ship this layer.
This pipeline produces images in the 8–15MB range with zero OS-level CVEs on build day. The CI/CD integration point matters here: the vulnerability scan should target the final stage image digest, not the builder stage. Many teams mistakenly scan the builder image and report its findings against the production artifact, inflating their risk metrics with vulnerabilities that never reach deployment.
Hardening Beyond the Base Layer: Runtime Security Controls
Choosing a minimal base image is necessary but not sufficient. Container hardening is a layered discipline, and the image composition is only the first control.
Non-Root Users, Read-Only Filesystems, and Capability Dropping
According to a 2026 Aqua Security threat intelligence report, over 60% of container escape exploits analyzed in the wild required the container process to be running as root. The fix is straightforward and should be a mandatory build requirement: every Dockerfile must include a USER directive specifying a non-root UID. Combined with a read-only root filesystem (–read-only flag or readOnlyRootFilesystem: true in Kubernetes security contexts), the persistence options for an attacker who achieves code execution inside the container shrink dramatically.
Linux capabilities provide fine-grained control over what privileged operations a container process can perform. Most applications require zero capabilities. Dropping all capabilities with –cap-drop=ALL and adding back only what is explicitly needed (e.g., NET_BIND_SERVICE for a service binding to port 443) enforces least-privilege at the kernel interface. Seccomp profiles — either the Docker default or a custom profile generated by tools like oci-seccomp-bpf-hook — restrict the syscall surface available to the container, preventing exploitation of kernel vulnerabilities through unusual syscall patterns.
Image Signing and Supply Chain Integrity
A hardened image is worthless if it can be substituted between build and deployment. The SLSA (Supply chain Levels for Software Artifacts) framework, now at Level 3 adoption by major enterprises, requires cryptographic attestation of build provenance. Sigstore’s cosign tool has emerged as the standard for container image signing in cloud-native environments. Images are signed with ephemeral keys anchored to OIDC identity (build system identity, not long-lived developer keys), producing a verifiable transparency log entry in the Rekor append-only ledger.
Kubernetes admission controllers — Kyverno or OPA/Gatekeeper — can enforce that only images with valid cosign signatures from your CI system are permitted to run in production clusters. This closes the substitution attack vector entirely, ensuring that the distroless image your pipeline built is the exact image your cluster executes.
Building a Continuous Image Hardening Pipeline
Container hardening is not a one-time activity. CVEs are published continuously, base images receive patches on irregular schedules, and application dependencies drift over time. The operational model must treat image security as a continuous posture, not a point-in-time audit.
Automated Vulnerability Scanning and Rebuild Triggers
An effective pipeline integrates vulnerability scanning at multiple gates. Trivy, Grype, or Snyk Container should run on every pull request, blocking merges when critical CVEs are introduced. The registry layer — whether Harbor, AWS ECR, or Google Artifact Registry — should perform continuous background scanning against stored image digests, triggering automated rebuild workflows when new CVEs are published against base image layers.
Chainguard’s Wolf images implement this with a “freshness guarantee”: every image is rebuilt daily from source, meaning the CVE window between vulnerability publication and patched image availability is measured in hours, not weeks. For teams building their own pipelines, tools like Renovate Bot can monitor base image tags and open automated PRs to update Dockerfile base references, keeping the security posture current with minimal manual overhead.
The metric to track is mean time to remediation for container CVEs (MTTR-C). Industry benchmarks from 2026 suggest elite DevSecOps teams achieve MTTR-C under 72 hours for critical findings. Teams relying on manual processes average 23 days — a gap that represents significant exploitability exposure in active threat environments.
Key Takeaways
- Fat base images are an inherited attack surface. The majority of container CVEs originate in OS packages the application never uses. Selecting Alpine, scratch, or distroless base images eliminates this inherited risk at the source.
- Multi-stage builds are the operational mechanism for distroless pipelines. Compile and test in a feature-rich builder stage; ship only the artifact in a hardened final stage. The builder image never touches production.
- Runtime hardening complements image minimalism. Non-root USER directives, read-only filesystems, cap-drop=ALL, and seccomp profiles each add independent layers of defense against code execution exploitation.
- Supply chain integrity requires cryptographic signing. Distroless images must be signed with cosign and verified by admission controllers to close the image substitution vector between CI and runtime.
- Container hardening is a continuous pipeline, not a one-time task. Automated rebuild triggers, registry-level scanning, and MTTR-C tracking transform image security from a point-in-time check into an operational posture.
Conclusion: Start Hardening Today, Not After the Breach
The infrastructure for building genuinely hardened container images — distroless base images, multi-stage builds, cosign signing, Trivy scanning, and Kubernetes admission enforcement — is mature, well-documented, and available without license cost. The barrier is not tooling; it is organizational inertia and the mistaken belief that container isolation provides sufficient security without deliberate image hardening.
The concrete action your team should take this sprint: audit your three highest-traffic production container images using trivy image –severity CRITICAL,HIGH [image-digest] and calculate how many of those findings live in base image layers versus application code. For most teams, the number is shocking enough to drive immediate prioritization of base image migration. From there, implement multi-stage builds targeting gcr.io/distroless or Alpine base layers, add cosign signing to your CI pipeline, and wire Kyverno admission policies to enforce signature verification in your staging cluster before rolling to production.
Your application code may be clean. Make sure the container carrying it is too.
💡 Enjoyed this article?
Subscribe for more expert insights delivered to your inbox.
Follow us or subscribe below xe2x80x94 free, no spam.





