Segurança em Docker na prática: lições aprendidas com incidentes reais

Como reduzir drasticamente riscos de segurança em contêineres Docker com práticas simples e eficazes

Compartilhe:

Era 23h de uma terça-feira quando uma ligação foi recebida.
O contêiner de um cliente havia sido comprometido.

Um invasor explorou uma vulnerabilidade no aplicativo Node.js, obteve acesso ao shell dentro do contêiner e, como ele estava sendo executado como root com privilégios excessivos, conseguiu acessar o sistema host.

Foram três horas de controle de danos.
Dados de clientes expostos.
Um relatório de incidente a ser redigido.

Tudo isso poderia ter sido evitado.

O problema é que, quando se trata de segurança no Docker, a maior parte das medidas não é complexa — apenas ignorada. As equipes colocam o contêiner em funcionamento, validam que “funciona”, fazem o deploy e a segurança fica para “resolver depois”.

O depois nunca chega… até que algo quebra.

Este guia reúne práticas que deveriam ter sido aplicadas antes daquela terça-feira à noite. Não exige conhecimento profundo em segurança, apenas passos práticos capazes de reduzir drasticamente o risco.

Apoie a Ramos da Informática

Café com Deus Pai Vol. 6 - 2026: Porções Diárias de Amor oferece 365 mensagens diárias que convidam você a um encontro íntimo com Deus, fortalecendo a fé e nutrindo a alma.

👉 Confira na Amazon .
Nas compras você contribui para manter o site no ar.

Por que a segurança no Docker é importante

Existe um equívoco comum de que contêineres são automaticamente seguros. Eles são isolados, certo?

Mais ou menos.

Os contêineres compartilham o kernel do host. Eles não são máquinas virtuais com isolamento completo. Uma vulnerabilidade de escape de contêiner — e elas existem — pode conceder a um invasor acesso ao sistema host e a todos os outros contêineres que rodam nele.

Mesmo sem escapar, um contêiner comprometido pode causar grandes danos:

  • Mineração de criptomoedas
  • Vazamento de dados
  • Movimentação lateral para serviços conectados
  • Uso do ambiente para lançar ataques contra outros sistemas

O objetivo do endurecimento de segurança é simples: se algo der errado, limitar o impacto. Dificultar o acesso e, caso alguém consiga entrar, garantir que o estrago possível seja mínimo.

Vamos dividir isso em etapas práticas.


Passo 1: Pare de executar como root

Esta é a mudança mais impactante que você pode fazer.

Por padrão, os processos dentro de contêineres Docker são executados como root. Não como um usuário limitado, mas como o usuário root real (UID 0).

Por que isso importa?

Se um invasor explorar sua aplicação e conseguir execução de código, ele terá acesso root dentro do contêiner. Poderá ler qualquer arquivo, modificar qualquer coisa e, se combinado com outras falhas, possivelmente escapar para o host.

Executar a aplicação como um usuário sem privilégios não impede a exploração inicial, mas reduz drasticamente o que um invasor pode fazer depois.

Como corrigir

Crie um usuário dedicado no Dockerfile e execute a aplicação com ele:

FROM node:20-alpine

WORKDIR /app

# Cria um usuário sem privilégios
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# Copia os arquivos ajustando o dono
COPY --chown=appuser:appgroup . .

# Instala dependências
RUN npm ci --only=production

# Usa o usuário não root
USER appuser

CMD ["node", "server.js"]

Pontos importantes:

  • addgroup e adduser criam grupo e usuário de sistema
  • A flag -S cria uma conta sem senha e sem diretório home
  • --chown garante permissões corretas nos arquivos
  • A instrução USER força todos os processos a rodarem sem privilégios

Algumas aplicações passam a apresentar erros de permissão após essa mudança. Normalmente isso acontece porque tentam escrever em diretórios onde não deveriam. Corrija a aplicação — não remova a segurança.

Verificação rápida

docker run myapp whoami

Se o resultado for root, ainda há trabalho a fazer.


Passo 2: Use imagens base mínimas

Cada pacote instalado no contêiner é uma superfície de ataque em potencial. Mais software significa mais vulnerabilidades, mais CVEs para corrigir e mais caminhos para um invasor.

É comum encontrar contêineres baseados em imagens completas do Ubuntu ou Debian, com centenas de pacotes que a aplicação nunca usa, mas que facilitam a vida de um atacante.

A solução

Use imagens base mínimas.

Alpine Linux:

FROM node:20-alpine

O Alpine é extremamente pequeno (cerca de 5 MB) e atende a maioria das aplicações.

Imagens distroless:

FROM gcr.io/distroless/nodejs20-debian12

Aqui não há shell, gerenciador de pacotes ou ferramentas extras. Mesmo que alguém consiga executar código, não há comandos básicos disponíveis.

Variantes slim:

FROM python:3.12-slim

Uma boa alternativa quando Alpine não é compatível.

Impacto

Uma imagem de mais de 1 GB baseada em Ubuntu frequentemente cai para menos de 100 MB. Menos pacotes, menos vulnerabilidades e uma superfície de ataque muito menor.


Passo 3: Escaneie suas imagens em busca de vulnerabilidades

Sua imagem base tem vulnerabilidades. Suas dependências também. Isso é normal.

O problema é não saber disso antes de um invasor.

Docker Scout

docker scout cves myapp:latest

Mostra vulnerabilidades conhecidas organizadas por gravidade.

Trivy

trivy image myapp:latest

O Trivy verifica:

  • Vulnerabilidades do sistema operacional
  • Dependências da aplicação
  • Erros de configuração
  • Segredos acidentalmente incluídos na imagem

Como lidar com os resultados

Na primeira execução, o volume de alertas pode assustar. Priorize:

  • Vulnerabilidades críticas e altas
  • Pacotes realmente usados pela aplicação
  • Falhas com exploração ativa conhecida

Atualize a imagem base e dependências e faça rebuild. Muitas falhas desaparecem apenas com versões atualizadas.

Automatize

Inclua o escaneamento no pipeline de CI e bloqueie deploys inseguros:

trivy image --exit-code 1 --severity CRITICAL myapp:${{ github.sha }}

Isso não é opcional — é higiene básica.


Passo 4: Nunca coloque segredos dentro da imagem

Ainda é comum encontrar senhas, tokens e chaves diretamente no Dockerfile. Isso é extremamente perigoso.

Qualquer pessoa com acesso à imagem pode extrair esses dados. Eles ficam armazenados no histórico da imagem, no registry e em sistemas de CI, mesmo que você os remova depois.

O que não fazer

ENV DATABASE_PASSWORD=supersecretpassword

Forma correta

Passe segredos apenas em tempo de execução ou use gerenciadores de segredos. Nada sensível deve existir dentro da imagem.


Passo 5: Use sistema de arquivos somente leitura

Se um invasor entra no contêiner, uma das primeiras ações é escrever arquivos: malware, backdoors, persistência.

Um sistema de arquivos somente leitura bloqueia isso imediatamente.

docker run --read-only myapp

Para diretórios que precisam ser graváveis, use tmpfs:

docker run --read-only --tmpfs /tmp --tmpfs /app/cache myapp

Isso impede:

  • Alteração do código da aplicação
  • Persistência de malware
  • Modificação de arquivos de configuração

Passo 6: Remova capabilities desnecessárias

Mesmo sem root, contêineres ainda possuem capacidades do Linux além do necessário.

Por padrão, eles podem:

  • Alterar dono de arquivos
  • Ignorar permissões
  • Usar portas privilegiadas

A maioria das aplicações não precisa disso.

docker run --cap-drop=ALL myapp

Adicione apenas o que for realmente necessário:

docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp

Nunca use --privileged em produção

docker run --privileged myapp

Isso praticamente desativa o isolamento do contêiner.


Passo 7: Defina limites de recursos

Sem limites, um contêiner pode consumir toda a CPU e memória do host — problema de performance e de segurança.

docker run --memory=512m --memory-swap=512m myapp
docker run --cpus=1.5 myapp

Isso impede ataques de negação de serviço e uso abusivo de recursos.


Passo 8: Isole suas redes

Por padrão, todos os contêineres em uma mesma rede Docker podem se comunicar livremente.

Separe redes por função:

  • Frontend → Backend → Database

Assim, se o frontend for comprometido, o banco não fica exposto diretamente.


Passo 9: Use perfis de segurança

Docker suporta mecanismos como seccomp e AppArmor.

Nunca desative:

docker run --security-opt seccomp=unconfined myapp

Ative também:

docker run --security-opt=no-new-privileges:true myapp

Isso impede escalonamento de privilégios.


Passo 10: Ative o Docker Content Trust

export DOCKER_CONTENT_TRUST=1

Garante que apenas imagens assinadas digitalmente sejam utilizadas.


Passo 11: Mantenha tudo atualizado

Docker Engine, imagens base e dependências acumulam vulnerabilidades ao longo do tempo.

Atualize regularmente e use ferramentas como Dependabot ou Renovate.


Exemplo completo de Dockerfile endurecido

FROM node:20.11.1-alpine3.19 AS builder

WORKDIR /app

COPY package.json package-lock.json ./
RUN npm ci --only=production

COPY . .
RUN npm run build

FROM node:20.11.1-alpine3.19

RUN apk add --no-cache dumb-init

WORKDIR /app

RUN addgroup -S appgroup && adduser -S appuser -G appgroup

COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/package.json ./

ENV NODE_ENV=production
USER appuser

EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD node -e "require('http').get('http://localhost:3000/health', r => process.exit(r.statusCode === 200 ? 0 : 1))"

ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/server.js"]

Checklist de segurança

Antes de ir para produção:

  • Usuário não root
  • Imagem base mínima
  • Nenhum segredo na imagem
  • Escaneamento de vulnerabilidades
  • Limites de CPU e memória
  • Sistema de arquivos protegido
  • Capabilities reduzidas
  • Redes isoladas
  • Dependências atualizadas

O custo real de ignorar segurança

O incidente citado no início custou cerca de US$ 10.000, considerando resposta ao incidente, comunicação com clientes, análise jurídica e correções.

As medidas deste guia? Algumas horas de trabalho.

Segurança não é paranoia. É realismo.

Comece pelo básico: não rodar como root e escanear imagens. Esses dois passos sozinhos já elevam drasticamente o nível de segurança. Depois, evolua gradualmente.

LEIA TAMBÉM:

Apoie a Ramos da Informática

Café com Deus Pai Vol. 6 - 2026: Porções Diárias de Amor oferece 365 mensagens diárias que convidam você a um encontro íntimo com Deus, fortalecendo a fé e nutrindo a alma.

👉 Confira na Amazon .
Nas compras você contribui para manter o site no ar.

Compartilhe:

Ramos Souza J
Ramos Souza Jhttps://ramosdainformatica.com.br/sobre/
Com mais de 26 anos de experiência em desenvolvimento de software, minha carreira é marcada por constante evolução tecnológica e pela entrega de soluções que fazem a diferença. Desde os primeiros passos com Clipper e Delphi até a consolidação em JavaScript e TypeScript, desenvolvi expertise em frameworks como Node.js, Nest e React, além de bancos de dados relacionais e não relacionais. Sou um Desenvolvedor Full Stack apaixonado por resolver problemas complexos com excelência técnica, adaptando-me rapidamente a novos desafios. Além do domínio técnico, sou reconhecido por meu relacionamento interpessoal e compromisso com resultados. Atualmente, trabalho em uma startup de Health-Tech e sou voluntário na OpenJS Foundation, promovendo o avanço do ecossistema JavaScript. Além de manter este site.
🔥 Oferta Gamer

NITRO V15

Poder Total em Suas Mãos

N
🚀
Processador
Ryzen 7 7735HS
🎮
GPU
RTX 4050 6GB
RAM + SSD
16GB + 512GB
🖥️
Tela
15.6" FHD 165Hz
Ray Tracing DLSS 3.0 DDR5 Wi-Fi 6 RGB Backlit
Confira na Amazon
OFERTA!
Ver na Amazon
✓ Entrega Rápida ✓ Garantia