Em abril de 2022, a empresa de segurança GitGuardian publicou um relatório revelando que mais de seis milhões de secrets foram expostos em repositórios públicos do GitHub apenas naquele ano — um aumento de 25% em relação ao ano anterior. Chaves de API da AWS, senhas de banco de dados, tokens de autenticação, credenciais de serviços de pagamento. A maioria deles ainda estava ativa no momento da descoberta.
A causa mais comum não é negligência deliberada. É a falta de um processo claro para gerenciar informações sensíveis em pipelines de CI/CD. Um desenvolvedor adiciona uma chave diretamente no arquivo de workflow para "testar rapidamente", o teste funciona, o commit vai para o repositório e a chave fica exposta para sempre no histórico — mesmo que seja removida em um commit posterior.
Entender como gerenciar secrets corretamente não é opcional. É uma competência fundamental de qualquer profissional de DevOps.
A Distinção entre Variáveis de Ambiente e Secrets
Antes de entrar nas ferramentas, é importante estabelecer uma distinção conceitual que muitas equipes ignoram.
Variáveis de ambiente são configurações que variam entre ambientes mas não são sensíveis. O nome do ambiente (NODE_ENV=production), a porta da aplicação (PORT=3000), a URL base de uma API pública, o nível de log (LOG_LEVEL=info) — todas essas informações podem viver em arquivos de configuração versionados sem risco.
Secrets são informações sensíveis cuja exposição representa um risco de segurança. Senhas de banco de dados, chaves de API, tokens OAuth, certificados, chaves SSH, chaves de criptografia — essas informações nunca devem aparecer em código, em logs ou em qualquer lugar que não seja um sistema de gerenciamento de secrets com controle de acesso e auditoria.
A confusão entre os dois conceitos é a origem de muitos vazamentos. Não basta proteger as senhas se as URLs de conexão com o banco contêm a senha embutida. Não basta não commitar o .env se o pipeline imprime variáveis de ambiente nos logs de debug.
Secrets no GitHub Actions
O GitHub oferece três níveis de secrets, cada um com escopo e visibilidade diferentes.
Repository secrets — disponíveis apenas no repositório onde foram criados. Adequados para projetos individuais ou segredos específicos de um repositório.
Environment secrets — vinculados a um ambiente específico (staging, production). Só ficam disponíveis quando o job está rodando naquele ambiente. Permitem ter a mesma chave com valores diferentes por ambiente.
Organization secrets — disponíveis para múltiplos repositórios dentro de uma organização. Adequados para segredos compartilhados como tokens de acesso a registros de container ou chaves de integração com ferramentas externas.
Criando um secret via GitHub CLI:
# Secret de repositório
gh secret set DATABASE_URL --body "postgresql://user:senha@host:5432/db"
# Secret de ambiente específico
gh secret set DATABASE_URL \
--env production \
--body "postgresql://user:senha_prod@host-prod:5432/db"
# A partir de um arquivo
gh secret set CHAVE_PRIVADA_SSH < ~/.ssh/id_ed25519
# Listando secrets existentes (apenas os nomes — valores nunca são exibidos)
gh secret list
gh secret list --env production
Usando Secrets em Workflows
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
# Forma 1: como variável de ambiente do step
- name: Conecta ao banco
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: npm run db:migrate
# Forma 2: como input de uma action
- name: Login no registro Docker
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Forma 3: escrevendo em arquivo temporário
- name: Configura chave SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.CHAVE_SSH_PRIVADA }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H ${{ secrets.SERVIDOR_HOST }} >> ~/.ssh/known_hosts
O GitHub Actions mascara automaticamente os valores de secrets nos logs. Se um secret tiver o valor minha-senha-secreta e esse valor aparecer em qualquer linha de log, ele será substituído por ***. No entanto, esse mascaramento tem limitações — valores muito curtos ou que aparecem como parte de strings maiores podem não ser mascarados corretamente.
O GITHUB_TOKEN: O Secret Automático
Todo workflow do GitHub Actions tem acesso automático a um token especial chamado GITHUB_TOKEN. Esse token é gerado pelo GitHub no início de cada execução de workflow e expirado ao final. Ele tem permissões para interagir com o repositório onde o workflow está rodando.
As permissões padrão do GITHUB_TOKEN podem ser configuradas no nível do repositório ou sobrescritas por workflow:
# Permissões mínimas para o workflow inteiro
permissions:
contents: read
packages: write
pull-requests: write
jobs:
build:
runs-on: ubuntu-latest
# Permissões específicas para este job
permissions:
contents: read
packages: write
steps:
- name: Login no GHCR usando o token automático
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
O princípio de menor privilégio se aplica diretamente aqui: cada workflow deve ter apenas as permissões que realmente precisa. Um workflow que apenas executa testes não precisa de permissão para escrever no repositório.
Variáveis de Ambiente por Ambiente
O GitHub Actions permite definir variáveis de ambiente em diferentes níveis de escopo, do mais amplo ao mais específico:
# Nível do workflow — disponível em todos os jobs
env:
NODE_VERSION: '20.x'
REGISTRY: ghcr.io
jobs:
build:
runs-on: ubuntu-latest
# Nível do job — disponível em todos os steps deste job
env:
NODE_ENV: production
APP_PORT: 3000
steps:
- name: Build
# Nível do step — disponível apenas neste step
env:
BUILD_TARGET: dist
run: npm run build -- --outDir $BUILD_TARGET
Para variáveis que mudam entre ambientes, o recurso de environment variables do GitHub Environments é o mais adequado:
No repositório, em Settings → Environments, cria-se o ambiente e adicionam-se variáveis não-sensíveis diretamente ali. Elas ficam disponíveis nos jobs que referenciam aquele ambiente:
jobs:
deploy:
runs-on: ubuntu-latest
environment: production # carrega variáveis e secrets deste ambiente
steps:
- name: Deploy
env:
# Variável definida no ambiente 'production' do GitHub
API_URL: ${{ vars.API_URL }}
# Secret definido no ambiente 'production'
API_KEY: ${{ secrets.API_KEY }}
run: ./scripts/deploy.sh
Gerenciadores Externos de Secrets
Para organizações com requisitos mais avançados de segurança ou com infraestrutura multi-cloud, os secrets do GitHub podem não ser suficientes. Os gerenciadores externos oferecem funcionalidades como rotação automática de credenciais, auditoria detalhada e integração com sistemas de identidade corporativos.
HashiCorp Vault é a solução open source mais consolidada. Integra com GitHub Actions via action oficial:
- name: Importa secrets do Vault
uses: hashicorp/vault-action@v3
with:
url: https://vault.minha-empresa.com
token: ${{ secrets.VAULT_TOKEN }}
secrets: |
secret/data/producao/banco password | DATABASE_PASSWORD;
secret/data/producao/api key | API_KEY;
secret/data/producao/redis url | REDIS_URL
AWS Secrets Manager é a solução gerenciada da AWS. Cada secret tem controle de acesso via IAM e suporte nativo a rotação automática:
- name: Configura credenciais AWS
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Importa secrets do AWS Secrets Manager
run: |
SECRET=$(aws secretsmanager get-secret-value \
--secret-id producao/minha-api \
--query SecretString \
--output text)
# Extrai valores individuais do JSON
DATABASE_URL=$(echo $SECRET | jq -r '.database_url')
API_KEY=$(echo $SECRET | jq -r '.api_key')
# Exporta para os steps seguintes de forma segura
echo "DATABASE_URL=$DATABASE_URL" >> $GITHUB_ENV
echo "API_KEY=$API_KEY" >> $GITHUB_ENV
echo "::add-mask::$DATABASE_URL"
echo "::add-mask::$API_KEY"
O comando ::add-mask:: instrui o GitHub Actions a mascarar aquele valor específico nos logs — essencial quando o valor vem de uma fonte externa e o GitHub não sabe automaticamente que deve ser mascarado.
O Arquivo .env em Desenvolvimento
Em desenvolvimento local, o arquivo .env é a forma mais prática de gerenciar configurações. A regra de ouro é simples: o .env nunca vai para o repositório.
Estrutura recomendada para projetos com múltiplos ambientes:
.env # Não versionado — valores locais do desenvolvedor
.env.example # Versionado — template com todas as chaves, sem valores
.env.test # Pode ser versionado — apenas valores de teste, sem dados reais
.env.production # Nunca versionado — gerenciado pelo pipeline
O .env.example serve como documentação viva do que o sistema precisa para funcionar:
# .env.example
# Banco de dados
DATABASE_URL=postgresql://usuario:senha@localhost:5432/nome_do_banco
# Cache
REDIS_URL=redis://localhost:6379
# Autenticação
JWT_SECRET=gere-um-valor-aleatorio-aqui
JWT_EXPIRES_IN=7d
# Serviço de email
SMTP_HOST=smtp.exemplo.com
SMTP_PORT=587
SMTP_USER=
SMTP_PASSWORD=
# Integrações externas
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
# Configurações da aplicação
NODE_ENV=development
PORT=3000
LOG_LEVEL=debug
A biblioteca dotenv carrega esse arquivo automaticamente em desenvolvimento:
// src/config/env.js
require('dotenv').config();
const config = {
database: {
url: process.env.DATABASE_URL,
},
redis: {
url: process.env.REDIS_URL,
},
jwt: {
secret: process.env.JWT_SECRET,
expiresIn: process.env.JWT_EXPIRES_IN || '7d',
},
server: {
port: parseInt(process.env.PORT || '3000'),
env: process.env.NODE_ENV || 'development',
},
};
// Valida que todas as variáveis obrigatórias estão presentes
const required = ['DATABASE_URL', 'REDIS_URL', 'JWT_SECRET'];
const missing = required.filter(key => !process.env[key]);
if (missing.length > 0) {
throw new Error(
`Variáveis de ambiente obrigatórias não definidas: ${missing.join(', ')}`
);
}
module.exports = config;
Essa validação na inicialização da aplicação evita que a aplicação suba silenciosamente com configurações incompletas e falhe de forma inesperada em runtime.
Auditoria e Rotação de Secrets
Secrets que nunca são rotacionados são um risco crescente. Um secret que foi exposto acidentalmente há seis meses pode ainda estar sendo explorado silenciosamente.
Boas práticas de ciclo de vida de secrets:
Rotação periódica — secrets de alto valor devem ser rotacionados regularmente, mesmo que não haja suspeita de comprometimento. O AWS Secrets Manager e o HashiCorp Vault suportam rotação automática.
Revogação imediata — quando um desenvolvedor sai da empresa ou quando um secret é acidentalmente exposto, a revogação deve ser imediata. Processos que dependem de secrets pessoais — chaves SSH de desenvolvedores individuais, tokens gerados por contas pessoais — são um risco operacional.
Auditoria de uso — saber quem acessou qual secret e quando é fundamental para investigação de incidentes. Gerenciadores como Vault e AWS Secrets Manager mantêm logs de acesso detalhados.
Scanning no código — ferramentas como o GitGuardian e o git-secrets verificam automaticamente se secrets foram commitados acidentalmente:
# Instala o git-secrets
brew install git-secrets # macOS
# ou compila da fonte no Linux
# Configura para verificar padrões da AWS
git secrets --register-aws
# Adiciona como hook pre-commit
git secrets --install
# Verifica o repositório inteiro em busca de secrets já commitados
git secrets --scan-history
Integrado ao pipeline de CI:
- name: Verifica secrets no código
run: |
pip install detect-secrets
detect-secrets scan --baseline .secrets.baseline
detect-secrets audit .secrets.baseline
Um Workflow Completo com Gerenciamento Seguro de Secrets
Reunindo todas as práticas em um workflow realista:
name: Deploy Seguro
on:
push:
branches: [main]
permissions:
contents: read
packages: write
id-token: write # necessário para OIDC com AWS
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
# Autenticação sem chaves estáticas via OIDC
- name: Configura credenciais AWS via OIDC
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/github-actions-deploy
aws-region: us-east-1
# Importa secrets do gerenciador externo
- name: Importa secrets de produção
run: |
SECRETS=$(aws secretsmanager get-secret-value \
--secret-id producao/minha-api \
--query SecretString --output text)
DB_URL=$(echo $SECRETS | jq -r '.database_url')
REDIS_URL=$(echo $SECRETS | jq -r '.redis_url')
# Mascara os valores nos logs
echo "::add-mask::$DB_URL"
echo "::add-mask::$REDIS_URL"
# Exporta para os steps seguintes
echo "DATABASE_URL=$DB_URL" >> $GITHUB_ENV
echo "REDIS_URL=$REDIS_URL" >> $GITHUB_ENV
# Build da imagem com o secret disponível apenas durante o build
- name: Constrói imagem Docker
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
secrets: |
database_url=${{ env.DATABASE_URL }}
# Deploy usando a imagem publicada
- name: Deploy no ECS
run: |
aws ecs update-service \
--cluster producao \
--service minha-api \
--force-new-deployment
# Limpa variáveis sensíveis do ambiente
- name: Limpa variáveis sensíveis
if: always()
run: |
echo "DATABASE_URL=" >> $GITHUB_ENV
echo "REDIS_URL=" >> $GITHUB_ENV
O Que Vem a Seguir
No próximo artigo será abordado o deploy automático para servidores com GitHub Actions — coberturas completas de estratégias de deploy, zero-downtime deployment, rollback automatizado e como estruturar o pipeline para que a promoção entre ambientes seja segura e auditável.
Referências para Aprofundamento
Segurança e boas práticas - GitHub Docs — Encrypted Secrets — Documentação oficial do GitHub sobre criação, uso e limitações dos secrets em Actions. - GitGuardian — State of Secrets Sprawl — Relatório anual sobre exposição de secrets em repositórios públicos. Leitura importante para dimensionar o problema. - OWASP — Secrets Management Cheat Sheet — Guia completo da OWASP sobre boas práticas de gerenciamento de secrets em diferentes contextos.
Ferramentas - HashiCorp Vault Documentation — Documentação completa do Vault, incluindo guias de instalação, políticas de acesso e integração com CI/CD. - AWS Secrets Manager — Developer Guide — Guia oficial do AWS Secrets Manager cobrindo criação, rotação e acesso a secrets. - detect-secrets — GitHub — Ferramenta open source do Yelp para detectar secrets em código. Mantém um baseline de falsos positivos conhecidos.
OIDC e autenticação sem chaves estáticas - GitHub Docs — Configuring OpenID Connect in AWS — Guia oficial para configurar autenticação OIDC entre GitHub Actions e AWS, eliminando a necessidade de chaves de acesso estáticas.