Skip to content

Back to the catalog

Securing Docker images
aikido.devarticle

Securing Docker images

Aikido Security's guide to hardening Docker images explains where container vulnerabilities come from (base OS layers like Debian/glibc that ship far more than an app needs), citing CVE-2023-4911 (Looney Tunables in glibc) and the xz-utils backdoor as cautionary examples for version-pinning decisions. It lays out hardening steps such as multi-stage builds, removing unused packages, non-root users, read-only filesystems, dropping Linux capabilities, pinning base images by digest, generating SBOMs, and signing/verifying images, then pitches Aikido Images, which backports CVE patches onto the same pinned base image version instead of forcing a migration to a new hardened distro.

TL;DR Practical Docker image hardening checklist (minimal bases, non-root, digest pinning, SBOM) framed around why base image vulnerability management is an ongoing job, from Aikido Security.
Document

What it is

A practitioner walkthrough (from Aikido) of Docker image hardening framed as ongoing vulnerability management, plus a specific critique of naive version strategies. Beyond the CVEs the summary lists, the page adds concrete decision logic about which version to pin and why stripping packages alone is insufficient.

The core distinction the summary omits

Removing unused packages only eliminates vulnerabilities in things you don't need. A vulnerable package your app actually depends on (e.g. glibc) can't be stripped and still requires a patch — and even a minimized image keeps accumulating new CVEs after build. So minimization and patching are two separate problems; doing the first does not solve the second.

Version strategy — both extremes fail

The page's actionable argument is that pinning-and-forgetting and always-upgrading are both wrong:

flowchart TD
    A[Choose base version] --> B{Strategy}
    B -->|Pin and forget| C[Node 20 EOL 2026-04-30:<br/>new CVEs never patched]
    B -->|Always newest| D[xz-utils backdoor in<br/>5.6.0 / 5.6.1; 5.4 was safe]
    B -->|Pin + record why + monitor| E[Know you run it and why;<br/>weigh exposure vs change cost]
  • Too old: node:20 reached end-of-life 2026-04-30; anything disclosed against that line since goes unpatched with no fix coming.
  • Too new: the xz-utils backdoor lived only in releases 5.6.0 and 5.6.1; teams on the older 5.4 line were never exposed. CISA's post-incident guidance was to downgrade, not upgrade.
  • Alpine note: because Alpine uses musl rather than glibc, it was never exposed to Looney Tunables (CVE-2023-4911) at all — base-distro choice can eliminate a class of CVE outright.

What to do

  • Start from a minimal/pre-hardened base — the highest-leverage choice.
  • Use multi-stage builds so compiler, dev headers, and package manager stay in an early stage and never ship in the final image.
  • Drop the shell (most apps exec their process directly) and runtime-unused utilities like curl/wget.
  • If you pin, record why you chose that version and keep checking it against new disclosures, so exposure is a known decision rather than an accident.

Caveats

The closing pitch (Aikido delivering patched rebuilds of the same base version via PR) is vendor marketing; the hardening principles stand independently.

Organisations named here

  • xz-utils

CVEs named here

  • CVE-2023-4911
  • CVE-2025-4373

Technologies named here

  • Aikido Security
  • glibc
  • Node.js 24
  • Aikido Images
  • Node.js 20
  • AutoFix
  • Aikido Intel
  • xz-utils 5.6.0 and 5.6.1
  • xz-utils 5.4 line
  • Aikido Libraries
  • Aikido container image scanning
  • debian:bookworm
  • Alpine Linux
  • SlimToolkit
  • CAP_NET_RAW
  • Docker
  • Node.js
  • Debian
  • SBOM

Techniques named here

  • Base image selection
  • Multi-stage builds
  • Removing unused packages
  • Non-root container execution
  • Read-only root filesystem
  • Dropping Linux capabilities
  • Secret handling via mounts
  • Runtime secret injection
  • Digest pinning
  • SBOM generation
  • Image signing and verification
  • Immutable running containers
  • Continuous rescanning
  • Backporting fixes
  • .dockerignore usage

Threat actors named here

  • XZ Utils backdoor