On January 31, 2024, researchers disclosed CVE-2024-21626, a flaw in the runc container runtime that let a malicious image break out of its sandbox and read arbitrary files on the host — a bug that had shipped quietly inside Docker and Kubernetes for months before anyone caught it. It's a reminder that a container image isn't just "the app"; it's every OS package, shared library, and binary bundled alongside it inside a single container format artifact, and each one is a potential entry point. Sysdig's 2023 Cloud-Native Security and Usage Report found that 87% of container images running in production contain at least one critical- or high-severity vulnerability, yet most teams still treat scanning as a pass/fail gate instead of a prioritization problem. Whether you call it container image security or secure container software, the underlying job is the same. Securing a container image comes down to three concrete steps, done in order: shrink what you ship, figure out which of your vulnerabilities are actually reachable, and stop unverified images from ever reaching a cluster. Here's how each of these remediation steps works in practice, and where teams usually get it wrong.
What are the three steps to securing a container image?
Securing a container image requires three steps performed in this order: minimize the base image, scan and prioritize the remaining vulnerabilities by exploitability rather than raw severity, and enforce signing and policy checks before the image is ever scheduled onto a cluster. Skipping the order matters more than most teams realize — scanning a bloated 1.2 GB ubuntu:latest image before trimming it produces a CVE list with hundreds of entries, most of them in packages the application never touches, which trains developers to ignore the scanner entirely. Doing the steps in sequence — reduce, prioritize, enforce — turns container security from a compliance checkbox into something a five-person platform team can actually keep up with. Each step below maps to a specific control you can put in place this quarter, not a multi-year program.
How do you reduce the attack surface of a container base image?
You reduce attack surface by switching from a full OS base image to a minimal or distroless image and stripping out anything the application doesn't need at runtime. A stock ubuntu:22.04 image carries roughly 180 installed packages — a shell, a package manager, curl, sudo — none of which a compiled Go binary or a Java service needs to run. Google's distroless images and Chainguard's wolfi-based images cut that down to the language runtime plus a handful of libraries, which in practice removes 60–95% of the CVEs a scanner would otherwise flag, simply because the vulnerable packages are no longer present. Multi-stage Docker builds make this easy to adopt without changing your build tooling: compile in a full SDK image, then COPY --from=builder only the compiled artifact into a distroless or scratch final stage — the resulting image is still a standard OCI container format artifact, so nothing about your registry, orchestrator, or existing tooling has to change to support it. Teams that made this switch after the 2021 Log4Shell disclosure (CVE-2021-44228) found that images without a bundled JRE or unused logging dependencies were never affected in the first place, because the vulnerable class simply wasn't there to exploit.
How do you know which vulnerabilities in a container image actually matter?
You know a vulnerability matters when the vulnerable function is actually reachable from your application's code paths at runtime — not merely present somewhere in the image's dependency tree. A typical enterprise container scan returns 200–800 CVEs per image; industry data from FIRST's Exploit Prediction Scoring System (EPSS) consistently shows that fewer than 5% of published CVEs have observed exploitation in the wild, yet legacy scanners rank all of them by CVSS score alone, which tells you nothing about whether the code path is ever executed. This gap got worse starting in February 2024, when NIST's National Vulnerability Database fell months behind on enrichment, leaving thousands of CVEs published without the metadata teams use to triage. Reachability analysis closes that gap by tracing your application's actual call graph — including transitive dependencies — against the vulnerable function, so a critical CVE in a logging library's XML parser gets deprioritized if your service never calls that parser, and a "medium" severity CVE in a package you call directly on every request gets escalated. Teams that adopt reachability-based triage typically cut their actionable remediation queue by 80–90%, turning an 800-CVE report into the 40–60 findings that are worth an engineer's time this sprint.
How do you generate an accurate SBOM for a container image?
You generate an accurate SBOM by producing it directly from your build tool at build time, not by disassembling the finished image after the fact. Executive Order 14028, signed in May 2021, and the follow-on NTIA minimum-elements guidance pushed SBOMs from a nice-to-have into a procurement requirement for any vendor selling software to the U.S. federal government, and most enterprise security teams now ask suppliers for one in SPDX or CycloneDX format before a contract is signed. Post-build scanning of a finished image can miss build-time dependencies that get flattened or stripped during compilation — for example, a Go binary statically links its dependencies, so a scanner looking only at the final layer has no way to see which version of a vulnerable module was actually compiled in. Generating the SBOM as a build artifact (via syft, language-native tooling, or a CI plugin) captures that information at the only point it's unambiguous, and storing it alongside the image in your registry means you can answer "are we affected by this new CVE" for every image you've ever shipped, in minutes, without re-scanning anything.
How do you stop vulnerable or unsigned container images from reaching production?
You stop them with an admission controller that enforces policy at deploy time, rejecting any image that fails a signature check or exceeds your vulnerability threshold before the Kubernetes scheduler ever places a pod. Docker deprecated Docker Content Trust in favor of the Sigstore project's cosign, which reached general availability in 2021 and is now the de facto standard for keyless image signing — CI pipelines sign images automatically using short-lived OIDC-backed certificates instead of long-lived private keys that can leak. Open Policy Agent's Gatekeeper and Kyverno both let platform teams write policy as code — for example, "reject any image without a valid cosign signature" or "reject any image with an unpatched critical CVE older than 30 days" — and enforce it cluster-wide rather than relying on individual teams to remember to scan before they merge. The combination matters: signing alone proves an image wasn't tampered with after your pipeline built it, but it says nothing about whether that pipeline built something insecure in the first place, which is why signature verification and vulnerability policy need to be checked together at the same admission gate.
How Safeguard Helps
Whether you frame these as remediation steps or as secure container software requirements, Safeguard automates all three instead of leaving them as separate tools your team has to stitch together. Our reachability analysis traces each CVE against your actual call graph so the findings that reach a Slack channel or a Jira ticket are the ones with a real exploitation path, not the 95% that live in unreachable code. Griffin AI, our security agent, reads the CVE, the reachable code path, and your codebase's dependency graph together, then opens an auto-fix pull request with the minimal version bump or patch needed to close the gap — no manual triage required to get from finding to fix. Safeguard also generates SPDX/CycloneDX SBOMs directly from your build pipeline and ingests SBOMs from vendors and existing scanners, so you get one accurate inventory across every image you build and every image you buy, without re-scanning finished containers and guessing at what got flattened during compilation.