Skip to main content

Cómo crear de forma segura una imagen Docker de una aplicación PHP

Escrito por
Headshot of James Olaogun

James Olaogun

feature docker secrets

2 de agosto de 2023

0 minutos de lectura

Nota del editor: 10 de agosto de 2023

Esta publicación se actualizó para incluir una versión más reciente de PHP en el tutorial.

Supongamos que desarrollaste una aplicación PHP, pero quieres separarla de la infraestructura de soporte de forma que sea liviana, portátil y bastante segura. Quieres que otros desarrolladores puedan trabajar en ella sin tener que recrear entornos completos. En resumen, lo que quieres hacer es crear un contenedor para tu aplicación: empaquetarla junto con sus dependencias en contenedores que se puedan compartir fácilmente entre distintos entornos.

La solución de contenedores más popular hoy en día es Docker, una plataforma de código abierto que se presentó en 2013. Desde entonces, se ha convertido en una tecnología ampliamente adoptada en el desarrollo de software para aislar aplicaciones, crear componentes de CI/CD reutilizables y permitir una implementación rápida.

Este tutorial te muestra paso a paso cómo crear contenedores de forma segura para una aplicación PHP o, más específicamente, cómo «dockerizarla». También usarás Snyk, una plataforma de seguridad para desarrolladores que ofrece herramientas para proteger aplicaciones modernas, identificar vulnerabilidades y automatizar su corrección.

Requisitos previos y recomendaciones

Antes de comenzar el siguiente tutorial, tómate un momento para asegurarte de tener estas herramientas:

  • Docker instalado en tu equipo. Lo usarás para ejecutar y administrar el contenedor PHP.

  • Una cuenta gratuita de Snyk. Las herramientas de análisis de seguridad de Snyk detectarán y corregirán vulnerabilidades en los contenedores.

  • NPM o Yarn para instalar y ejecutar la Snyk CLI.

  • Una aplicación PHP básica. En este tutorial se usa un proyecto de Laravel recién creado.

Cuando tengas la seguridad de que cumples esos requisitos, considera los siguientes consejos antes de continuar con el tutorial. Estas recomendaciones te ayudarán a dockerizar tu aplicación PHP de forma sencilla y segura.

Usa una versión específica de la imagen para las compilaciones

Asegúrate siempre de usar una versión específica de la imagen Docker al compilar tu aplicación. Por ejemplo, en lugar de descargar PHP-FPM, puedes especificar la versión de la imagen y descargar PHP:8.0-fpm. Ten en cuenta que la imagen base que uses dependerá de los requisitos de versión de tu aplicación.

Así, puedes tener la certeza de que tu aplicación se compila sobre una versión conocida, probada y estable. Además, sabrás que la versión de la imagen puede usar la misma plataforma subyacente y las mismas dependencias en distintos entornos, lo que puede mejorar la reproducibilidad y la coherencia.

No uses ADD para dependencias remotas

El comando ADD puede descargar archivos desde cualquier URL, lo que puede introducir vulnerabilidades de seguridad en tu aplicación dockerizada. Por eso, recomiendo evitar el comando `ADD` en los Dockerfile para incluir dependencias remotas.

En su lugar, usa el comando COPY para copiar archivos desde el sistema de archivos local. El comando COPY garantiza que solo se incluyan en la imagen Docker los archivos necesarios para la aplicación. De nuevo, esto ayuda a reducir el riesgo de vulnerabilidades.

Usa un archivo YAML de docker-compose

Docker Compose te permite administrar y orquestar tus contenedores Docker mediante la definición de tu aplicación Docker en un archivo YAML y la especificación de cómo debe compilarse y ejecutarse. Algunos beneficios de usar Docker Compose son:

  • Separar los componentes de la aplicación. Docker Compose te permite separar los componentes de tu aplicación en distintos contenedores. Esto limita la superficie de ataque de cada contenedor y reduce el riesgo de un único punto de falla.

  • Usar variables de entorno. Docker Compose te permite usar variables de entorno para configurar tu aplicación, lo que puede ayudarte a administrar de forma segura información confidencial, como las contraseñas de bases de datos.

  • Administrar las conexiones de red. Docker Compose te permite administrar las conexiones de red entre tus contenedores. Esto garantiza que solo puedan comunicarse entre sí mediante un canal seguro.

Si instalaste Docker Desktop para Windows, Mac o Linux, ya tienes Docker Compose.

Usa PHP-FPM, no mod_php

PHP-FPM y mod_php son soluciones para ejecutar aplicaciones PHP en servidores web, pero funcionan de manera diferente. mod_php es un módulo de Apache que permite que el servidor web Apache procese archivos PHP, mientras que PHP-FPM es un administrador de procesos PHP que funciona con varios servidores web, incluidos NGINX y Apache.

Por lo general, PHP-FPM se considera una forma más eficiente y escalable de ejecutar aplicaciones PHP, especialmente en entornos con mucho tráfico.

Usa compilaciones de varias etapas

Las compilaciones de varias etapas son una técnica de Docker para crear imágenes más pequeñas y seguras. Al crear varias etapas dentro de un Dockerfile, cada una con sus propias instrucciones, no tienes que volver a compilar toda la imagen desde cero cada vez que haces un cambio. Como resultado, mejoras la eficiencia y la velocidad del proceso de compilación de Docker.

Prueba siempre la seguridad de tu aplicación dockerizada

Las pruebas de seguridad frecuentes son fundamentales para garantizar y mejorar la seguridad de tu aplicación. Aunque Docker ofrece un entorno aislado para ejecutarla, eso no garantiza que sea segura. Realiza comprobaciones de seguridad periódicas para identificar y abordar posibles vulnerabilidades antes de que los atacantes puedan aprovecharlas.

Cinco pasos para dockerizar tu aplicación PHP de forma segura

¡Pasemos a la parte práctica! En el siguiente tutorial, te guiaremos por cinco pasos para dockerizar de forma segura una aplicación PHP de Laravel, con algunos de los consejos que acabamos de ver.

1. Configura el Dockerfile con Apache

Si todavía no tienes un proyecto, crea un nuevo proyecto de Laravel y cambia al directorio del proyecto con cd. Por ejemplo, en el siguiente comando, reemplaza {{laravel-application-directory}} por el nombre del directorio de tu aplicación Laravel.

cd {{laravel-application-directory}} 

Crea un archivo llamado Dockerfile en el directorio raíz de tu aplicación Laravel:

touch Dockerfile

Copia y pega el siguiente código en el Dockerfile que acabas de crear:

FROM composer:latest as composer
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --prefer-dist --no-scripts --no-dev --no-autoloader && \
    composer clear-cache
COPY . .
RUN composer dump-autoload --no-scripts --no-dev --optimize

# Depending on the composer you use, you may be required to use a different php version.
FROM php:8.2-apache
ENV APACHE_DOCUMENT_ROOT=/var/www/html/public

# 1. Install production packages and clean up apt cache.
RUN apt-get update && apt-get install -y \
    libbz2-dev \
    libfreetype6-dev \
    libicu-dev \
    libjpeg-dev \
    libmcrypt-dev \
    libpng-dev \
    libonig-dev \
    libzip-dev \
    libreadline-dev \
    sudo \
    zip \
 && rm -rf /var/lib/apt/lists/*

# 2. Apache configs + document root.
RUN echo "ServerName laravel-app.local" >> /etc/apache2/apache2.conf
RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf
RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf

# 3. mod_rewrite for URL rewrite and mod_headers for .htaccess extra headers like Access-Control-Allow-Origin-
RUN a2enmod rewrite headers

# 4. Start with base PHP config, then add extensions.
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"

RUN docker-php-ext-install \
    bcmath \
    bz2 \
    calendar \
    iconv \
    intl \
    mbstring \
    opcache \
    pdo_mysql \
    zip

# 5. Copy the application code from the composer stage.
COPY --from=composer /app /var/www/html

# 6. Create a non-root user to run the application.
ARG uid
RUN useradd -G www-data,root -u $uid -d /home/devuser devuser
RUN mkdir -p /home/devuser/.composer && \
    chown -R devuser:devuser /home/devuser

USER devuser
RUN echo "export PATH=$PATH:/var/www/html/vendor/bin" >> /home/devuser/.bashrc

Aunque, como se indicó anteriormente, PHP-FPM es la mejor opción para compilar una solución escalable, este tutorial usa mod_php porque la aplicación es pequeña.

También observa que el contenido del Dockerfile anterior usa compilaciones de varias etapas. La primera etapa usa la imagen oficial de Composer para instalar las dependencias de la aplicación y compilar el autocargador. La segunda etapa usa la imagen oficial de PHP/Apache para ejecutar la aplicación. Ten en cuenta que se usa el comando COPY para copiar los archivos de compilación de Composer desde el sistema de archivos local, no el comando ADD.

Crea un archivo llamado docker-compose.yml en el directorio raíz de tu aplicación Laravel:

Touch docker-compose.yml

Copia y pega el siguiente código en el archivo docker-compose.yml que acabas de crear:

version: '3'

services:
  snykdemoapp:
    build:
      context: '.'
      args:
        uid: 1000
    container_name: snykdemoapp
    image: snykdemoapp
    ports:
      - 8000:80
    environment:
      - APACHE_RUN_USER=#1000
      - APACHE_RUN_GROUP=#1000
    volumes:
      - .:/var/www/html
    networks:
      app-network:
        aliases:
          - snykdemoapp

networks:
  app-network:
    name: app-network

En el código anterior, el servicio snykdemoapp es el contenedor de la aplicación PHP. Además, la sección de redes define app-network, que Docker Compose usará para la comunicación.

2. Configura un contenedor de base de datos MySQL en docker-compose.yml

Agreguemos un contenedor MySQL a tu archivo docker-compose.yml. Este contenedor alojará la base de datos de tu aplicación Laravel.

Crea un servicio para tu contenedor MySQL agregando el siguiente código como nuevo servicio al archivo docker-compose.yml que creaste en el primer paso:

app_db:
    image: mysql:5.7
    container_name: app_db
    volumes:
      - ./run/var:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=securerootpassword
      - MYSQL_DATABASE={{database_name}} 
      - MYSQL_USER={{database_user}}
      - MYSQL_PASSWORD={{dbuser_password}}
    networks:
      app-network:
        aliases:
          - app_db

En el bloque de código anterior:

  • app_db es el nombre del servicio de base de datos, creado con la imagen de MySQL versión 5.7. Puedes cambiar el nombre como prefieras, pero asegúrate de actualizarlo en los demás servicios que estén vinculados.

  • {{database_name}} es el nombre de base de datos que prefieras.

  • {{database_user}} es el usuario de base de datos que prefieras.

  • {{dbuser_password}} es la contraseña que prefieras para el usuario de base de datos.

¡Listo! Agregaste correctamente un contenedor MySQL a tu configuración de Docker.

3. Configura un contenedor de PHPMyAdmin en docker-compose.yml

Ahora agreguemos un contenedor de PHPMyAdmin para pruebas y desarrollo local, junto con tus contenedores de Laravel y MySQL. PHPMyAdmin es una herramienta de administración gratuita y de código abierto para MySQL y MariaDB. Ofrece una interfaz web para administrar tus bases de datos. Se recomienda usar PHPMyAdmin solo durante el desarrollo, no en producción.

Crea un servicio para tu contenedor de PHPMyAdmin agregando el siguiente fragmento de código como nuevo servicio a docker-compose.yml.

phpmyadmin:
    image: phpmyadmin/phpmyadmin
    links:
      - app_db
    environment:
      PMA_HOST: app_db
      PMA_PORT: 3306
    ports:
      - '8080:80'
    networks:
      app-network:
        aliases:
          - phpmyadmin

En el bloque de código anterior, phpmyadmin es el nombre del servicio de PHPMyAdmin, creado con la imagen phpmyadmin/phpmyadmin. La sección de entorno establece la variable PMA_HOST en app_db, que es el nombre del servicio MySQL en el mismo archivo docker-compose.yml.

Ahora tienes un contenedor de PHPMyAdmin configurado para funcionar con tus contenedores de Laravel y MySQL. Ten en cuenta que la imagen de PHPMyAdmin solo debe usarse con fines de desarrollo. Usarla en producción supone un riesgo de seguridad para tu aplicación.

4. Compila y ejecuta la aplicación PHP de Laravel con Docker

Ahora que configuraste el Dockerfile y docker-compose.yml, inicia el servicio Docker y asegúrate de que esté en ejecución. Luego, puedes compilar y ejecutar la aplicación PHP de Laravel con los siguientes comandos:

  • docker-compose build: compila la imagen Docker.

  • docker-compose up -d: inicia los contenedores.

  • docker ps: comprueba el estado del contenedor.

Si todos los comandos anteriores se ejecutan correctamente, abre el navegador y ve a http://localhost:8000/ para acceder a la página principal de tu aplicación Laravel.

Página de bienvenida de Laravel con enlaces a Documentation, Laracasts, Laravel News y su vibrante ecosistema.

También puedes ir a http://localhost:8080/ para acceder a la interfaz de PHPAdmin. Puedes usar MYSQL_USER y MYSQL_PASSWORD, que configuraste en el archivo docker-compose.yml, para iniciar sesión.

Pantalla de bienvenida de phpMyAdmin con el inglés seleccionado y campos de nombre de usuario y contraseña para iniciar sesión

5. Prueba y protege tu aplicación PHP con Snyk

Cuando hayas compilado correctamente tu aplicación Docker y puedas acceder tanto a ella como a PHPMyAdmin desde el navegador, el siguiente paso es probar su seguridad. En este tutorial se usa Snyk para probar y garantizar que la aplicación dockerizada esté completamente protegida.

Inicia sesión o crea una cuenta de Snyk con cualquiera de las opciones de SSO que aparecen en la página. Instala Snyk CL y autentica tu laptop, como se indica en la sección de requisitos previos de este artículo.

Pantalla de registro de Snyk con opciones de GitHub y Google junto a un panel que describe el análisis de vulnerabilidades y sus correcciones.

Una vez que hayas configurado Snyk, usa Snyk CLI para evaluar la seguridad de tu aplicación dockerizada. Ejecuta el siguiente comando en el directorio raíz de tu aplicación Laravel:

snyk container test {{your-image}} –file=Dockerfile

En la línea anterior, {{your-image}} es el nombre de la imagen que quieres probar. Puedes usar el comando `docker ps` para obtener una lista de las imágenes Docker de tu aplicación.

Al ejecutar el comando anterior en la imagen base PHP 8.2 llamada snykdemoapp que se usa en este tutorial, obtendrás lo siguiente:

Resultados del análisis de la imagen de contenedor de PHP 8.2 que muestran el estado de las vulnerabilidades de seguridad en la prueba de Snyk Container.

PHP 8.2 es la versión más reciente con soporte a largo plazo. Aunque se encontraron 69 problemas de seguridad en la imagen base de este contenedor, todos son de gravedad baja.

Si usaras versiones anteriores de PHP en tus aplicaciones de producción, como PHP 7.4, encontrarías vulnerabilidades de seguridad más graves y urgentes que requieren atención. En este caso, Snyk es aún más útil porque te ofrece tipos de imágenes alternativos con menos vulnerabilidades.

El comando snyk container test solo ofrece un resultado puntual. Una mejor opción es ejecutar el comando container monitor para analizar el contenedor.

snyk container monitor {{your-image}}

El comando container monitor envía a Snyk la información de las pruebas de la imagen de tu contenedor. Así, Snyk puede mostrarla en tu panel y notificarte sobre posibles vulnerabilidades futuras en la imagen.

Al ejecutar container monitor en la imagen base PHP 7.4 llamada snykdemoapp.

Panel del proyecto de Snyk que muestra el manifiesto composer.lock de una imagen de Docker y una lista de 113 dependencias

Cómo corregir las vulnerabilidades detectadas en tu aplicación PHP

Ahora que probaste tu contenedor de PHP, aquí tienes algunos consejos para corregir las vulnerabilidades detectadas.

  • Vuelve a crear la imagen para obtener las actualizaciones de seguridad más recientes de la imagen base de Docker original. Así te aseguras de que tu aplicación incluya todas las actualizaciones de seguridad. Después de volver a crear la imagen, ejecuta el análisis de seguridad otra vez para identificar y corregir las vulnerabilidades que puedan venir con las actualizaciones de seguridad más recientes de la imagen base.

  • Consulta la evaluación de vulnerabilidades que ofrece Snyk. Esta lista puede proporcionar información útil sobre posibles problemas de seguridad en las dependencias de la aplicación.

  • Ten en cuenta que las vulnerabilidades pueden provenir de distintas fuentes. Entre ellas se encuentran el entorno de ejecución de PHP, así como las herramientas y bibliotecas instaladas en la imagen base. Es fundamental actualizar y monitorear periódicamente todos los componentes de la pila de la aplicación para identificar y corregir cuanto antes cualquier vulnerabilidad de seguridad.

Conclusión

Seguir algunas prácticas recomendadas puede ayudarte a desarrollar aplicaciones con menos vulnerabilidades. Si completaste el tutorial paso a paso, deberías poder desarrollar de forma segura una aplicación PHP en Docker, usando la plataforma de seguridad de Snyk para detectar y corregir vulnerabilidades.

Puedes encontrar en GitHub todo el código que se usó para la aplicación Laravel en Docker de este tutorial.

Seguridad de contenedores centrada en los desarrolladores

Snyk encuentra y corrige automáticamente vulnerabilidades en imágenes de contenedores y cargas de trabajo de Kubernetes.

Leer más

feature customer snowflake
Article

Seguridad desde el inicio: presentamos la integración de Snyk Studio con Snowflake Cortex Code

Snyk Studio se integra con Snowflake Cortex Code para analizar código generado por IA, dependencias y contenedores en busca de vulnerabilidades durante el desarrollo.

blog feature pypi spoof
Article

Toda la Snyk AI Security Platform, gratis para quienes mantienen proyectos de código abierto

Quienes mantienen proyectos de código abierto reciben reportes de vulnerabilidades reales sin parar y necesitan ayuda para priorizarlas, corregirlas y publicar soluciones más rápido. El Secure Developer Program de Snyk ofrece a los proyectos que cumplen los requisitos acceso gratis a Snyk AI Security Platform.

feature insights announcement
Blog

Compromiso de la cadena de suministro de node-gyp: un gusano de npm que se propaga solo y se oculta en binding.gyp

Un nuevo gusano de npm abusa de binding.gyp para activar node-gyp durante la instalación y permitir que paquetes maliciosos ejecuten código sin scripts del ciclo de vida. Roba credenciales, persiste en GitHub y se propaga entre mantenedores.