Skip to main content

Como colocar um aplicativo PHP em um contêiner Docker com segurança

Escrito por
Headshot of James Olaogun

James Olaogun

feature docker secrets

2 de agosto de 2023

0 minutos de leitura

Nota do editor: 10 de agosto de 2023

Esta publicação foi atualizada para incluir uma versão mais recente do PHP no tutorial.

Imagine que você criou um aplicativo PHP, mas quer separá-lo da infraestrutura de suporte de um jeito que mantenha tudo leve, portátil e ainda bastante seguro. Você quer que outros desenvolvedores possam trabalhar nele sem precisar recriar ambientes inteiros. Em resumo, o que você quer fazer é colocá-lo em um contêiner — empacotando o aplicativo e suas dependências em contêineres que podem ser compartilhados facilmente entre ambientes.

A solução de conteinerização mais popular atualmente é o Docker, uma plataforma de código aberto lançada em 2013. Desde então, ela se tornou uma tecnologia amplamente adotada no desenvolvimento de software para isolar aplicativos, criar componentes reutilizáveis de CI/CD e permitir implantações rápidas.

Neste tutorial, você vai aprender passo a passo como colocar com segurança um aplicativo PHP em um contêiner — ou, mais especificamente, “Dockerizá-lo”. Você também vai usar o Snyk, uma plataforma de segurança para desenvolvedores com ferramentas que ajudam a identificar vulnerabilidades em aplicativos modernos e automatizar suas correções.

Pré-requisitos e recomendações

Antes de começar o tutorial, reserve um momento para garantir que você tem as seguintes ferramentas:

  • O Docker instalado na sua máquina. Você vai usá-lo para executar e gerenciar o contêiner PHP.

  • Uma conta gratuita do Snyk. As ferramentas de análise de segurança do Snyk vão identificar e corrigir vulnerabilidades nos contêineres.

  • NPM ou Yarn para instalar e executar a Snyk CLI.

  • Um aplicativo PHP básico. Neste tutorial, usamos um projeto Laravel recém-criado.

Depois de confirmar que você atende a esses requisitos, considere as dicas a seguir antes de continuar. Estas recomendações devem ajudar você a Dockerizar seu aplicativo PHP com segurança e sem complicações.

Use uma versão específica da imagem como base das compilações

Sempre use uma versão específica da imagem Docker ao compilar seu aplicativo. Por exemplo, em vez de baixar PHP-FPM, você pode especificar a versão da imagem e baixar PHP:8.0-fpm. Lembre-se de que a imagem base usada dependerá dos requisitos de versão do seu aplicativo.

Assim, você garante que seu aplicativo seja compilado com base em uma versão conhecida, testada e estável. Além disso, você saberá que a versão da imagem pode usar a mesma plataforma e as mesmas dependências subjacentes em diferentes ambientes, o que melhora a reprodutibilidade e a consistência.

Não use ADD para dependências remotas

O comando ADD pode baixar arquivos de URLs arbitrárias, o que pode introduzir vulnerabilidades de segurança no seu aplicativo Dockerizado. Por isso, recomendo evitar o comando `ADD` nos Dockerfiles para incluir dependências remotas.

Em vez disso, use o comando COPY para copiar arquivos do sistema de arquivos local. O comando COPY garante que apenas os arquivos necessários para o aplicativo sejam incluídos na imagem Docker. Mais uma vez, isso ajuda a reduzir o risco de vulnerabilidades.

Use um arquivo YAML do Docker Compose

O Docker Compose permite gerenciar e orquestrar seus contêineres Docker definindo o aplicativo Docker em um arquivo YAML e especificando como ele deve ser compilado e executado. Entre os benefícios do Docker Compose estão:

  • Separação dos componentes do aplicativo. O Docker Compose permite separar os componentes do aplicativo em diferentes contêineres. Isso limita a superfície de ataque de cada contêiner e reduz o risco de um único ponto de falha.

  • Uso de variáveis de ambiente. O Docker Compose permite usar variáveis de ambiente para configurar seu aplicativo, o que pode ajudar você a gerenciar informações confidenciais com segurança, como senhas de banco de dados.

  • Gerenciamento de conexões de rede. O Docker Compose permite gerenciar as conexões de rede entre seus contêineres. Assim, eles só podem se comunicar entre si por um canal seguro.

Se você instalou o Docker Desktop para Windows, Mac ou Linux, o Docker Compose já está disponível.

Use PHP-FPM, não mod_php

PHP-FPM e mod_php são soluções para executar aplicativos PHP em servidores web, mas funcionam de maneiras diferentes. Enquanto mod_php é um módulo do Apache que permite ao servidor web Apache processar arquivos PHP, PHP-FPM é um gerenciador de processos PHP compatível com vários servidores web, incluindo NGINX e Apache.

Em geral, o PHP-FPM é considerado uma maneira mais eficiente e escalável de executar aplicativos PHP, especialmente em ambientes com alto tráfego.

Use compilações em várias etapas

A compilação em várias etapas é uma técnica do Docker para criar imagens menores e mais seguras. Ao criar várias etapas em um Dockerfile, cada uma com seu próprio conjunto de instruções, você não precisa recompilar a imagem inteira do zero a cada alteração. Com isso, o processo de compilação do Docker fica mais rápido e eficiente.

Sempre teste a segurança do seu aplicativo Dockerizado

Testes de segurança frequentes são fundamentais para garantir e melhorar a segurança do seu aplicativo. Embora o Docker ofereça um ambiente isolado para executar o aplicativo, isso não significa que ele seja seguro. Faça verificações de segurança regularmente para identificar e corrigir possíveis vulnerabilidades antes que invasores possam explorá-las.

Cinco etapas para colocar seu aplicativo PHP em um contêiner Docker com segurança

Agora vamos à parte prática! Neste tutorial, você vai aprender cinco etapas para Dockerizar com segurança um aplicativo PHP com Laravel, usando algumas das dicas que acabamos de apresentar.

1. Configure o Dockerfile usando o Apache

Crie um novo projeto Laravel, caso ainda não tenha um, e use cd para acessar o diretório do projeto. Por exemplo, no comando a seguir, substitua {{laravel-application-directory}} pelo nome do diretório do seu aplicativo Laravel.

cd {{laravel-application-directory}} 

Crie um arquivo chamado Dockerfile no diretório raiz do seu aplicativo Laravel:

touch Dockerfile

Copie e cole o código a seguir no Dockerfile recém-criado:

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

Embora o PHP-FPM seja a melhor opção para criar uma solução escalável, como mencionado acima, este tutorial usa mod_php porque o aplicativo é pequeno.

Observe também que o conteúdo do Dockerfile acima usa a compilação em várias etapas. Na primeira etapa, a imagem oficial do Composer instala as dependências do aplicativo e cria o autoloader. Na segunda, a imagem oficial do PHP/Apache executa o aplicativo. Observe que usamos o comando COPY para copiar os arquivos de compilação do Composer do sistema de arquivos local, e não o comando ADD.

Crie um arquivo chamado docker-compose.yml no diretório raiz do seu aplicativo Laravel:

Touch docker-compose.yml

Copie e cole o código a seguir no arquivo docker-compose.yml recém-criado:

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

No código acima, o serviço snykdemoapp é o contêiner do aplicativo PHP. Além disso, a seção networks define a rede app-network, que o Docker Compose usará para a comunicação.

2. Adicione um contêiner de banco de dados MySQL ao docker-compose.yml

Vamos adicionar um contêiner MySQL ao seu arquivo docker-compose.yml. Esse contêiner hospedará o banco de dados do seu aplicativo Laravel.

Crie um novo serviço para o contêiner MySQL adicionando o código a seguir como um novo serviço ao arquivo docker-compose.yml criado na primeira etapa:

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

No bloco de código acima:

  • app_db é o nome do serviço de banco de dados criado com a imagem da versão 5.7 do MySQL. Você pode escolher outro nome, mas lembre-se de alterá-lo também nos outros serviços que o referenciam.

  • {{database_name}} é o nome de banco de dados que você preferir.

  • {{database_user}} é o nome de usuário do banco de dados que você preferir.

  • {{dbuser_password}} é a senha que você preferir para o usuário do banco de dados.

Pronto: você adicionou um contêiner MySQL à sua configuração do Docker.

3. Adicione um contêiner PHPMyAdmin ao docker-compose.yml

Agora vamos adicionar um contêiner PHPMyAdmin para desenvolvimento e testes locais, junto aos contêineres Laravel e MySQL. O PHPMyAdmin é uma ferramenta de administração gratuita e de código aberto para MySQL e MariaDB. Ele oferece uma interface web para gerenciar seus bancos de dados. Recomendamos usar o PHPMyAdmin apenas em desenvolvimento, não em produção.

Crie um novo serviço para o contêiner PHPMyAdmin adicionando o trecho de código abaixo como um novo serviço ao arquivo 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

No bloco de código acima, phpmyadmin é o nome do serviço PHPMyAdmin criado com a imagem phpmyadmin/phpmyadmin. A seção environment define a variável PMA_HOST como app_db, que é o nome do serviço MySQL no mesmo arquivo docker-compose.yml.

Agora você tem um contêiner PHPMyAdmin configurado para funcionar com seus contêineres Laravel e MySQL. Lembre-se de que a imagem PHPMyAdmin deve ser usada apenas para desenvolvimento. Usá-la em produção representa um risco de segurança para seu aplicativo.

4. Compile e execute o aplicativo PHP com Laravel no Docker

Agora que você configurou o Dockerfile e o arquivo docker-compose.yml, inicie o serviço Docker e verifique se ele está em execução. Em seguida, você pode compilar e executar o aplicativo PHP com Laravel usando os comandos a seguir:

  • docker-compose build: compila a imagem Docker.

  • docker-compose up -d: inicia os contêineres.

  • docker ps: verifica o status do contêiner.

Se todos os comandos acima forem executados com sucesso, abra o navegador e acesse http://localhost:8000/ para visitar a página inicial do seu aplicativo Laravel.

Página de boas-vindas do Laravel com links para a documentação, Laracasts, Laravel News e seu vibrante ecossistema.

Você também pode acessar http://localhost:8080/ para abrir a interface do PHPAdmin. Use os valores de MYSQL_USER e MYSQL_PASSWORD definidos no arquivo docker-compose.yml para fazer login.

Tela de boas-vindas do phpMyAdmin com o inglês selecionado e campos de nome de usuário e senha para fazer login

5. Teste e proteja seu aplicativo PHP com o Snyk

Depois de compilar seu aplicativo Docker com sucesso e conseguir acessar tanto o aplicativo quanto o PHPMyAdmin pelo navegador, a próxima etapa é testar a segurança do aplicativo Dockerizado. Neste tutorial, usamos o Snyk para testar e garantir que o aplicativo Dockerizado esteja totalmente protegido.

Entre na sua conta ou crie uma nova conta do Snyk usando qualquer uma das opções de SSO listadas na página. Instale a Snyk CLI e autentique seu laptop, conforme indicado na seção de pré-requisitos deste artigo.

Tela de cadastro do Snyk com opções do GitHub e do Google ao lado de um painel que descreve a análise e a correção de vulnerabilidades.

Depois de configurar o Snyk, use a Snyk CLI para avaliar a segurança do seu aplicativo Dockerizado. Execute o comando a seguir no diretório raiz do seu aplicativo Laravel:

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

Na linha acima, {{your-image}} é o nome da imagem que você quer testar. Você pode usar o comando `docker ps` para ver uma lista das imagens Docker do seu aplicativo.

Ao executar o comando acima na imagem base PHP 8.2 chamada snykdemoapp usada neste tutorial, você verá o seguinte:

Resultados da verificação de uma imagem de contêiner PHP 8.2, mostrando o status das vulnerabilidades de segurança no teste do Snyk Container.

O PHP 8.2 é a versão mais recente com suporte de longo prazo (LTS). Embora tenham sido encontrados 69 problemas de segurança na imagem base deste contêiner, todos são de baixa gravidade.

Se você usasse versões anteriores do PHP em aplicativos de produção, como o PHP 7.4, encontraria vulnerabilidades de segurança mais graves e urgentes que precisariam ser corrigidas. Nesse caso, o Snyk se torna ainda mais valioso, pois oferece imagens alternativas com menos vulnerabilidades.

O comando snyk container test fornece apenas um resultado pontual. Uma opção melhor é executar o comando container monitor para fazer uma análise do contêiner.

snyk container monitor {{your-image}}

O comando container monitor envia ao Snyk as informações do teste da imagem do seu contêiner. Assim, o Snyk pode exibir essas informações no seu painel e avisar você sobre possíveis vulnerabilidades futuras na imagem.

Ao executar container monitor na imagem base PHP 7.4 chamada snykdemoapp.

Painel de projetos da Snyk mostrando o manifesto composer.lock de uma imagem Docker e uma lista com 113 dependências

Como corrigir vulnerabilidades detectadas no seu aplicativo PHP

Agora que você testou seu contêiner PHP, confira algumas dicas para corrigir as vulnerabilidades detectadas.

  • Reconstrua a imagem para obter as atualizações de segurança mais recentes da imagem base upstream do Docker. Assim, todas as atualizações de segurança serão incorporadas ao seu aplicativo. Depois de reconstruir a imagem, execute a verificação de segurança novamente para identificar e corrigir possíveis vulnerabilidades introduzidas pelas atualizações mais recentes da imagem base.

  • Confira a avaliação de vulnerabilidades fornecida pela Snyk. Essa lista pode trazer informações úteis sobre possíveis problemas de segurança nas dependências do aplicativo.

  • Lembre-se de que as vulnerabilidades podem ter várias origens. Elas podem estar no runtime do PHP, nas ferramentas ou nas bibliotecas instaladas na imagem base. É fundamental atualizar e monitorar regularmente todos os componentes da pilha do aplicativo para identificar e corrigir vulnerabilidades de segurança o quanto antes.

Conclusão

Seguir algumas práticas recomendadas pode ajudar você a desenvolver um aplicativo com menos vulnerabilidades. Se você acompanhou o tutorial passo a passo, já deve conseguir desenvolver com segurança um aplicativo PHP em contêiner Docker, usando a plataforma de segurança da Snyk para detectar e corrigir vulnerabilidades.

Você encontra todo o código usado no aplicativo Laravel em contêiner Docker deste tutorial no GitHub.

Segurança de contêineres que prioriza os desenvolvedores

O Snyk encontra e corrige automaticamente vulnerabilidades em imagens de contêiner e workloads do Kubernetes.

Leia mais

feature customer snowflake
Article

Segurança desde o início: conheça a integração do Snyk Studio com o Snowflake Cortex Code

O Snyk Studio se integra ao Snowflake Cortex Code para detectar vulnerabilidades em código gerado por IA, dependências e contêineres durante o desenvolvimento.

blog feature pypi spoof
Article

A Snyk AI Security Platform completa, grátis para mantenedores de projetos open source

Mantenedores de projetos open source estão sobrecarregados com relatórios reais de vulnerabilidades e precisam de ajuda para priorizar, corrigir e lançar correções mais rápido. O Secure Developer Program da Snyk dá aos projetos qualificados acesso gratuito à Snyk AI Security Platform.

feature insights announcement
Blog

Comprometimento da cadeia de suprimentos do node-gyp: um worm npm que se autopropaga e se esconde em binding.gyp

Um novo worm do npm está explorando binding.gyp para acionar o node-gyp durante a instalação, permitindo que pacotes maliciosos executem código sem scripts de ciclo de vida. Ele rouba credenciais, mantém persistência no GitHub e se propaga entre contas de mantenedores.