Una imagen de Docker se compone de una pila de capas, que son conjuntos de cambios en el sistema de archivos de solo lectura e inmutables apilados sobre las inferiores. En la base se sitúa un sistema operativo base, seguido de los paquetes necesarios para su ejecución y, por último, tu propio código. Proteger las imágenes de Docker implica controlar su contenido y mantenerlas parcheadas después de su despliegue.
La mayoría de las vulnerabilidades de un contenedor provienen de los paquetes y bibliotecas de código abierto con los que se distribuye. Algunas, como cURL o una shell, es posible que la aplicación nunca las llame una vez que el contenedor esté en marcha, por lo que se pueden eliminar de la imagen. Otras, como glibc, son necesarias para que la aplicación funcione y no se pueden quitar. El resto de las vulnerabilidades suelen ser consecuencia de errores humanos, como un secreto integrado en una capa o un contenedor que se deja ejecutando con privilegios de root.
Este artículo analiza de dónde provienen los riesgos en una imagen de Docker, por qué mantener las imágenes endurecidas es un problema de gestión de vulnerabilidades, las tres formas en que las herramientas mantienen parcheada una imagen base y cómo Aikido endurece y parchea las imágenes que ya estás ejecutando.
{{cta}}
TL;DR
La protección de imágenes de Docker implica eliminar los paquetes que no se utilicen y parchear las vulnerabilidades del resto. Eliminar los paquetes no utilizados solo mitiga las vulnerabilidades de aquello que no necesitas. Un paquete vulnerable del que tu aplicación realmente depende sigue necesitando un parche, e incluso una imagen optimizada sigue acumulando nuevas vulnerabilidades. Además, adoptar imágenes endurecidas suele implicar migrar a otra distribución y actualizar según el ritmo de terceros. Aikido ofrece nuevas compilaciones parcheadas de las mismas versiones de imágenes base, entregadas mediante PR. Si has fijado una versión, esto te permite mantenerte en ella sin sufrir cambios disruptivos ni tener que migrar a una imagen nueva.
De dónde provienen los riesgos en una imagen de Docker
El FROM la línea es la primera instrucción en un archivo Dockerfile y define la imagen base sobre la que estás construyendo. Un FROM node:24 establece la imagen oficial de Node.js 24 como base, la cual incluye el entorno de ejecución de Node.js junto con un sistema Debian completo por debajo. Esto incluye una shell, un gestor de paquetes y bibliotecas del sistema, cada uno de los cuales puede incorporar vulnerabilidades que heredas antes de escribir una sola línea de tu propio código.
Cada instrucción de Dockerfile añade una capa sobre la anterior, y las capas son inmutables. Eliminar algo en una capa posterior solo lo oculta, por lo que todo lo que la imagen base incluía permanece en ella, tanto si tu aplicación lo usa como si no. Es probable que tu aplicación nunca invoque la mayor parte de lo que viene en la base, pero cada paquete presente constituye una superficie de ataque.
Por ejemplo, CVE-2023-4911 («Looney Tunables») fue un fallo de escalada de privilegios local en el cargador dinámico de glibc, un componente situado en la capa base de prácticamente cualquier imagen de Linux convencional. glibc venía incluido en la distribución y cualquier contenedor construido sobre una base afectada lo heredaba.
Diferentes bases, diferentes riesgos
No todas las imágenes base incluyen la misma cantidad de componentes. Una imagen de distribución completa incluye mucho más de lo que tu aplicación necesita, incluyendo una shell y un gestor de paquetes que probablemente nunca utilizará. Una variante slim reduce parte de ello. Una imagen distroless prescinde por completo de la shell y del gestor de paquetes, conservando únicamente lo necesario para el entorno de ejecución. Alpine es pequeña por diseño y, al estar construida sobre musl en lugar de glibc, ni siquiera estuvo expuesta a Looney Tunables.
Mantener las imágenes endurecidas es un problema de gestión de vulnerabilidades
Una imagen endurecida está limpia el día que la compilas, pero se degrada con el tiempo a medida que se descubren nuevas vulnerabilidades en los paquetes que ya contiene. Mantenerla segura implica tratar la imagen como un elemento que debes volver a comprobar frente a nuevas divulgaciones y parchear de forma continua. La versión manual implica rastrear las divulgaciones en cada paquete de cada base y aplicar un backport o actualizar cada corrección de forma manual.
Fijar una versión de confianza tiene sentido cuando realmente has probado esa versión y quieres seguir ejecutándola, pero aun así necesitas monitorizar nuevas divulgaciones. Una imagen que se fija y se olvida acumulará vulnerabilidades descubiertas recientemente. Si esa imagen llega al final de su ciclo de vida y deja de recibir parches del proveedor, su lista de fallos conocidos y sin parchear aumenta. Por ejemplo, Node.js 20 llegó al final de su ciclo de vida el 30 de abril de 2026, por lo que un contenedor que sigue fijado a node:20 sigue ejecutándose, pero cada vulnerabilidad divulgada para esa línea desde entonces queda sin parchear y sin ninguna solución en camino.
El instinto contrario, saltar siempre a la versión más reciente, tiene el mismo fallo. Por ejemplo, la puerta trasera de xz-utils es una biblioteca de compresión que se incluye dentro de la mayoría de las imágenes base de Linux. Estaba presente en dos versiones recientes específicas, la 5.6.0 y la 5.6.1, y los equipos que seguían en la línea anterior 5.4 nunca se vieron expuestos. La directriz de la CISA posterior fue hacer un downgrade, no una actualización.
Lo que sí te mantiene a salvo es tomar decisiones meditadas sobre las versiones que ejecutas. Tomemos a un equipo que fija debian:bookworm, registra que la eligió por encima de la versión más reciente porque la probaron y es estable, y la comprueba frente a las nuevas divulgaciones a medida que aparecen. Cuando se divulga una nueva CVE en un paquete de esa imagen, ya saben que la ejecutan y por qué. Eso les permite sopesar la exposición real frente al coste del cambio.
Cómo asegurar imágenes de Docker
Reducir la superficie de ataque
Comienza desde una imagen base mínima o preendurecida. La base determina la mayor parte de lo que heredas, por lo que es la decisión de mayor impacto que tomas. (Se trata con más profundidad en la siguiente sección)
Utiliza compilaciones en múltiples etapas (multi-stage builds) para mantener las herramientas de compilación fuera de la imagen final. La compilación de la imagen normalmente incluye un compilador, cabeceras de desarrollo y un gestor de paquetes para compilar tu aplicación e instalar sus dependencias. En una compilación de una sola etapa, todo eso se queda integrado en la imagen final, a pesar de que la aplicación en ejecución no lo utiliza nunca. Una compilación en múltiples etapas genera el artefacto en una etapa inicial y copia únicamente eso en una etapa final limpia, por lo que ninguna herramienta de compilación se despliega.
Elimina los paquetes que la aplicación en ejecución no necesite. Es probable que no necesites un shell, ya que la mayoría de las aplicaciones inician su proceso directamente y nunca llaman a uno en tiempo de ejecución. Las utilidades como cURL o wget son útiles al compilar, pero probablemente no las necesites en tiempo de ejecución, ya que tu aplicación realiza sus llamadas de red a través de sus propias bibliotecas. Y cualquier elemento que cURL haya obtenido durante la compilación ya está integrado en la imagen en el momento de su despliegue, por lo que no queda nada por descargar.
Configurar los permisos correctamente
Ejecuta el contenedor como un usuario que no sea root. Si un atacante consigue ejecutar código en tu aplicación, obtiene los privilegios con los que se ejecuta el contenedor. No ser root reduce el radio de impacto de una brecha.
Utiliza un sistema de ficheros raíz de solo lectura. Evita que un atacante que ya esté dentro pueda escribir ficheros, desplegar herramientas o modificar el contenedor en tiempo de ejecución. Cualquier cosa que necesite escribir de verdad puede recibir un montaje de escritura específico en su lugar.
Desactiva las capacidades de Linux que no utilices. Los contenedores empiezan con un conjunto de capacidades del núcleo que la mayoría de las aplicaciones nunca utilizan. CAP_NET_RAW, por ejemplo, permite que un proceso falsifique paquetes de red y viene activada por defecto, pero una aplicación web típica nunca la necesita, mientras que un atacante que logre entrar podría usarla para suplantar tráfico en tu red. Elimina las que no necesites y añade únicamente las que sí. Descartar estas capacidades (y añadir de vuelta solo lo necesario) bloquea operaciones privilegiadas que de otro modo estarían disponibles para un atacante.
Evita que secretos y archivos dispersos lleguen a la imagen
Evita que los secretos queden en las capas. Un secreto introducido mediante ARG o COPY permanece en el historial de la imagen incluso si una capa posterior lo elimina, y cualquiera que descargue la imagen puede descomprimir las capas y leerlo. Utiliza montajes de secretos o montajes SSH para exponer un secreto a un único paso de compilación sin escribirlo en ninguna capa.
Mantener un secreto fuera de las capas es solo la mitad del trabajo. El contenedor en ejecución todavía necesita el valor, así que inyéctalo en tiempo de ejecución desde un gestor de secretos dedicado (Vault, un gestor de secretos en la nube o secretos de Kubernetes) montado como archivo o inyectado en el entorno, y rótalo periódicamente en lugar de incrustar una credencial de larga duración en ninguna parte.
Utiliza un archivo .dockerignore . Controla qué entra en el contexto de compilación desde el principio, para que .gitla configuración local y las credenciales dispersas nunca se copien en la imagen mediante un comando COPY instrucción.
Fija las versiones con cuidado
Fija la imagen base por resumen (digest), no por etiqueta. FROM node:24 se vuelve a resolver con el tiempo, por lo que dos compilaciones con un mes de diferencia pueden descargar imágenes distintas, y una base comprometida puede colarse sigilosamente. Fijar por resumen (@sha256:...) hace que la base sea determinista, de modo que compilas exactamente la misma imagen que probaste. Ten en cuenta que si tu modelo de parcheo distribuye correcciones como nuevas imágenes, un resumen fijado también retiene esas correcciones. Si en su lugar puedes obtener una compilación parcheada de la misma versión que fijaste, evitas ese problema y te mantienes seguro.
No dependas de latest. Algunos asumen que significa la versión más nueva y estable, pero en Docker es simplemente la etiqueta por defecto que se aplica a la última imagen subida sin una etiqueta de versión explícita. Puede apuntar a una imagen más antigua de lo que esperas y puede cambiar bajo tus pies entre compilaciones.
Verifica y mantén
Genera un SBOM para cada imagen. Una lista de materiales de software es el inventario completo de lo que hay dentro de la imagen, cada paquete y versión. Es lo que te permite responder "¿estamos afectados?" en el momento en que se divulga una nueva vulnerabilidad.
Firma cada imagen al compilarla y verifica esa firma antes de ejecutarla con un controlador de admisión que rechace cualquier elemento sin firmar. Esa cadena evita que una imagen manipulada o no aprobada llegue a producción, incluso si alguien logra infiltrarse en tu registro.
Trata los contenedores en ejecución como inmutables. Una vez desplegada una imagen, no la modifiques ni reconfigure el contenedor en ejecución. Compila una nueva imagen, pruébala y vuelve a desplegar. Lo que esté en marcha coincidirá siempre con lo que compilaste, probaste y firmaste, lo que elimina la deriva de configuración y hace que un rollback sea tan sencillo como desplegar de nuevo la imagen anterior.
Revisa las imágenes de forma continuada. Una imagen que se analizó sin problemas la semana pasada puede contener una vulnerabilidad crítica hoy, ya que cada día se divulgan nuevas vulnerabilidades en paquetes que ya contiene. Por lo tanto, comprobar solo en el momento de la compilación no es suficiente.
Tres formas de mantener una imagen base protegida
Mantener una imagen base protegida a medida que se descubren nuevas vulnerabilidades es una tarea continua, y existen tres formas de gestionarlo.
Hacerlo por tu cuenta
Mantén tu propia base protegida. Elige una imagen minimalista, elimina lo que no necesites y restrúcturala cada vez que un paquete incluido presente una vulnerabilidad. Mantienes el control total, pero requiere trabajo y mantenimiento continuos. Haces un seguimiento de las divulgaciones de cada paquete en la base y aplicas o actualizas cada corrección manualmente. Multiplica eso por cada base en cada servicio, y consumirá el tiempo de cualquier otra cosa que podrías estar construyendo.
Cambiar a una distribución reconstruida por un proveedor
Algunas plataformas ofrecen imágenes protegidas reconstruidas sobre su propia distribución. Migras tus servicios a su base, vuelves a probar para detectar fallos y luego te adaptas a su ritmo de lanzamientos, avanzando hacia un nuevo resumen (digest) con cada corrección. Esto conlleva dos costes. La propia migración puede romper cualquier funcionalidad de la que dependiera tu aplicación y que el proveedor haya eliminado. Además, mantenerse parcheado implica la misma trampa de actualización que surge con cualquier cambio de dependencia, donde una nueva versión principal puede mover rutas de paquetes, cambiar valores predeterminados y romper un Dockerfile funcional. Si fijas un resumen por estabilidad, no obtienes los parches para los cuales adoptaste la herramienta.
Dejar que una herramienta parchee la base que ya ejecutas
Algunos proveedores te permiten mantener tu versión actual proporcionando compilaciones parcheadas de esa misma versión en lugar de obligarte a pasar a una nueva imagen. Obtienes una compilación nueva de la misma versión con el CVE parcheado, la cual adoptas revisándola y fusionándola como cualquier otro cambio, sin necesidad de editar la imagen en ejecución. De este modo, te mantienes en la versión que ya probaste y aun así solucionas la vulnerabilidad.
Cómo protege Aikido las imágenes de Docker
Aikido Security ofrece nuevas compilaciones parcheadas de las mismas versiones de imágenes base que ya ejecutas.

Aikido Images es un registro de más de 2000 imágenes de reemplazo directo en las que las vulnerabilidades críticas y de alta gravedad conocidas en la base ya están parcheadas. Cada una se reconstruye, parchea, minimiza y protege durante la compilación, por lo que obtienes una superficie de ataque más pequeña y configuraciones predeterminadas seguras en la base que ya utilizas.
Mientras que una herramienta de distribución reconstruida ofrece el parche como una nueva imagen a la que debes migrar, Aikido aplica la corrección a la versión que has fijado. Toma la corrección de la versión más reciente y la aplica a la versión que ya ejecutas, de modo que el cambio sea mínimo y la imagen se comporte exactamente igual que siempre. Cuando no es posible aplicar una corrección limpia, Aikido actualiza o reconstruye el componente en su lugar. El intercambio es un reemplazo directo que AutoFix propone como una solicitud de extracción (pull request), y cada descarga desde docker.aikido.io incluye un SBOM, un VEX y procedencia SLSA.
Sigue parcheando versiones incluso después de que el upstream haya dejado de darles soporte, incluidas las bases que han llegado al final de su ciclo de vida (end-of-life), para que puedas conservar una versión anterior sin arrastrar sus fallos conocidos y no tengas que actualizar solo para mantenerte cubierto.
Un porcentaje cada vez mayor de las correcciones reales para los paquetes nunca recibe un CVE, por lo que si tú o tus herramientas solo leen la base de datos de CVE, os estaréis perdiendo vulnerabilidades. El sistema de parches de Aikido se alimenta en parte de Aikido Intel, que lee los registros de cambios (changelogs) y el historial de confirmaciones (commits) del upstream para detectar vulnerabilidades que se solucionaron discretamente en el origen sin un CVE asociado, por lo que la cobertura no se limita a lo que las bases de datos públicas hayan alcanzado a documentar.
Los paquetes a nivel de aplicación en npm, PyPI, Maven y Go se parchean directamente a través de Aikido Libraries, utilizando el mismo enfoque de retrocompatibilidad aplicado a tus dependencias. Y Aikido Container Image Scanning comprueba si hay paquetes del SO vulnerables, dependencias, runtimes obsoletos, malware y riesgos de licencias en las imágenes base, los comandos de Dockerfile y las cargas de trabajo de Kubernetes, de modo que cualquier hallazgo se muestre junto con el contexto de código y cloud.
El equipo detrás de estas imágenes se unió a Aikido tras la adquisición de Root, la empresa creadora de SlimToolkit (anteriormente DockerSlim), una de las herramientas de endurecimiento de imágenes de código abierto más utilizadas. El endurecimiento de imágenes fue el punto de partida de este equipo, y la herramienta sigue siendo gratuita y abierta para que cualquiera pueda utilizarla.
{{walkthrough}}
Preguntas frecuentes
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "TechArticle",
"@id": "https://www.aikido.dev/blog/securing-docker-images#article",
"isPartOf": { "@id": "https://www.aikido.dev/blog/securing-docker-images#webpage" },
"mainEntityOfPage": { "@id": "https://www.aikido.dev/blog/securing-docker-images#webpage" },
"headline": "Securing Docker images: harden, patch, and maintain them",
"description": "Most of a container's vulnerabilities come from the base image. How to harden Docker images, why hardening is ongoing, and how to patch the base you already run.",
"datePublished": "2026-08-28",
"dateModified": "2026-08-28",
"wordCount": 1650,
"timeRequired": "PT8M",
"inLanguage": "en-US",
"author": { "@id": "https://www.aikido.dev/authors/nicholas-thomson#person" },
"publisher": { "@id": "https://www.aikido.dev/#organization" },
"image": { "@id": "https://www.aikido.dev/blog/securing-docker-images#primaryimage" },
"articleSection": "DevSec Tools & Comparisons",
"keywords": [
"securing Docker images",
"Docker image security",
"container image hardening",
"base image vulnerabilities",
"distroless images",
"backporting CVE fixes",
"container vulnerability management",
"non-root container",
"multi-stage builds",
"SBOM"
],
"about": [
{ "@type": "Thing", "name": "Docker image security" },
{ "@type": "Thing", "name": "Container image hardening" },
{ "@type": "Thing", "name": "Vulnerability management" }
],
"mentions": [
{ "@type": "SoftwareApplication", "name": "Docker", "applicationCategory": "DeveloperApplication" },
{ "@type": "SoftwareApplication", "name": "Aikido Images", "applicationCategory": "SecurityApplication", "url": "https://www.aikido.dev/cloud/hardened-images" },
{ "@type": "SoftwareApplication", "name": "Aikido Libraries", "applicationCategory": "SecurityApplication" },
{ "@type": "Thing", "name": "Alpine Linux" },
{ "@type": "Thing", "name": "Debian" },
{ "@type": "Thing", "name": "glibc" },
{ "@type": "Thing", "name": "Node.js" },
{ "@type": "Thing", "name": "SBOM" },
{ "@type": "Thing", "name": "SLSA provenance" },
{ "@type": "Thing", "name": "VEX" },
{ "@type": "Thing", "name": "CVE-2023-4911", "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-4911" },
{ "@type": "Thing", "name": "CVE-2025-4373" }
],
"speakable": {
"@type": "SpeakableSpecification",
"cssSelector": ["h1", "h2"]
}
},
{
"@type": "WebPage",
"@id": "https://www.aikido.dev/blog/securing-docker-images#webpage",
"url": "https://www.aikido.dev/blog/securing-docker-images",
"name": "Securing Docker images: harden, patch, and maintain them",
"description": "Most of a container's vulnerabilities come from the base image. How to harden Docker images, why hardening is ongoing, and how to patch the base you already run.",
"inLanguage": "en-US",
"isPartOf": { "@id": "https://www.aikido.dev/#website" },
"primaryImageOfPage": { "@id": "https://www.aikido.dev/blog/securing-docker-images#primaryimage" },
"breadcrumb": { "@id": "https://www.aikido.dev/blog/securing-docker-images#breadcrumb" },
"datePublished": "2026-08-28",
"dateModified": "2026-08-28"
},
{
"@type": "ImageObject",
"@id": "https://www.aikido.dev/blog/securing-docker-images#primaryimage",
"url": "https://www.aikido.dev/blog/securing-docker-images/hero.png",
"caption": "Securing Docker images"
},
{
"@type": "BreadcrumbList",
"@id": "https://www.aikido.dev/blog/securing-docker-images#breadcrumb",
"itemListElement": [
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "https://www.aikido.dev" },
{ "@type": "ListItem", "position": 2, "name": "Blog", "item": "https://www.aikido.dev/blog" },
{ "@type": "ListItem", "position": 3, "name": "Securing Docker images", "item": "https://www.aikido.dev/blog/securing-docker-images" }
]
},
{
"@type": "WebSite",
"@id": "https://www.aikido.dev/#website",
"url": "https://www.aikido.dev",
"name": "Aikido Security",
"publisher": { "@id": "https://www.aikido.dev/#organization" },
"inLanguage": "en-US"
},
{
"@type": "Organization",
"@id": "https://www.aikido.dev/#organization",
"name": "Aikido Security",
"url": "https://www.aikido.dev",
"logo": {
"@type": "ImageObject",
"url": "https://www.aikido.dev/logo.png"
},
"sameAs": [
"https://www.linkedin.com/company/aikido-security",
"https://x.com/AikidoSecurity"
]
},
{
"@type": "Person",
"@id": "https://www.aikido.dev/authors/nicholas-thomson#person",
"name": "Nicholas Thomson",
"url": "https://www.aikido.dev/authors/nicholas-thomson",
"jobTitle": "Senior SEO & Growth Lead",
"worksFor": { "@id": "https://www.aikido.dev/#organization" },
"sameAs": [
"https://www.linkedin.com/in/nicholas-gray-thomson/"
]
},
{
"@type": "FAQPage",
"@id": "https://www.aikido.dev/blog/securing-docker-images#faq",
"isPartOf": { "@id": "https://www.aikido.dev/blog/securing-docker-images#webpage" },
"mainEntity": [
{
"@type": "Question",
"name": "What is the most important step to secure a Docker image?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Choosing a small base image. Most of a container's vulnerabilities come from the OS packages the base ships, not the code you wrote, so a smaller base means fewer inherited flaws before you add anything of your own. A distroless or minimal base drops whole classes of vulnerabilities that a full distro image would drag in."
}
},
{
"@type": "Question",
"name": "Should Docker containers run as root?",
"acceptedAnswer": {
"@type": "Answer",
"text": "No, unless something genuinely requires it. If an attacker gets code execution in your app, they inherit whatever privileges the container runs as, and root in the container plus a container escape can mean root on the host. Set a dedicated non-root user in your Dockerfile and drop the Linux capabilities the app never uses."
}
},
{
"@type": "Question",
"name": "What is the difference between a minimal image and a hardened image?",
"acceptedAnswer": {
"@type": "Answer",
"text": "A minimal image ships less, stripping out shells, package managers, and libraries the app doesn't need. A hardened image starts from a minimal base and goes further, applying patched package versions and secure defaults like non-root execution. Minimal shrinks the attack surface, and hardened shrinks it and locks down what's left."
}
},
{
"@type": "Question",
"name": "Do I have to migrate to a new distro to harden my base image?",
"acceptedAnswer": {
"@type": "Answer",
"text": "No. Some hardening tools rebuild images on their own distribution, which means migrating every service and re-testing for breakage. The alternative is to keep the distro and major version you already run and backport fixes to that exact version, which is how Aikido Images works, so you get the patch without the migration."
}
},
{
"@type": "Question",
"name": "Why does a hardened image become vulnerable again over time?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Because new vulnerabilities are disclosed against packages already inside it. Nothing in the image changed, but the list of known flaws in its packages keeps growing. Hardening is an ongoing job rather than a one-time pass."
}
},
{
"@type": "Question",
"name": "Can a secret end up inside a Docker image by accident?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes, and it is a common mistake. A secret pulled in with ARG or COPY stays in the image history even if a later layer deletes it, so anyone who pulls the image can unpack the layers and read it. Use build-time secret mounts during the build, and inject runtime secrets from a secrets manager rather than baking them into the image."
}
}
]
}
]
}
</script>