Como gerenciar secrets no GitHub Actions: do risco de vazamento à implantação sem chaves com OIDC

Em um fim de semana de março de 2025, a equipe de segurança do GitHub enviou um e-mail urgente aos responsáveis por mais de 23.000 repositórios.
Os secrets deles poderiam ter vazado.
O responsável era uma action chamada tj-actions/changed-files. A ferramenta, amplamente usada, havia sido comprometida por invasores e roubava silenciosamente todas as variáveis de ambiente e secrets dos workflows. Talvez você esteja pensando: meu projeto também usa essa action; será que fui afetado?
O incidente colocou em evidência uma questão que muita gente ignora: afinal, como gerenciar secrets no GitHub Actions?
Vamos tratar de três pontos: como escolher entre os três níveis de secrets, como aplicar oito regras fundamentais de segurança e como o OIDC permite abandonar de vez o risco de vazamento de credenciais estáticas.
1. Os três níveis de GitHub Actions Secrets
O GitHub oferece três níveis para armazenar secrets: Repository, Environment e Organization. Qual escolher? Depende do seu cenário.
Repository Secrets: a primeira opção para projetos pessoais
Este é o nível mais simples. Os secrets ficam no repositório e todos os workflows podem acessá-los. Para um projeto pessoal, um repositório monolítico ou um projeto sem vários ambientes de implantação, isso basta.
A única desvantagem é não conseguir diferenciar um secret com o mesmo nome em staging e production. Se você tem um DATABASE_URL com valores diferentes nesses dois ambientes, precisa usar Environment Secrets.
Environment Secrets: essenciais para vários ambientes
Environment Secrets permitem separar os secrets por ambiente e também oferecem fluxos de aprovação. Nas configurações do repositório do GitHub, você pode criar os ambientes staging e production e definir secrets diferentes para cada um.
Uma característica importante: somente um job que faz referência ao ambiente pode acessar os secrets correspondentes. Isso deixa os limites de segurança mais claros.
jobs:
deploy-staging:
runs-on: ubuntu-latest
environment: staging # faz referência ao ambiente staging
steps:
- run: echo "Deploying to staging..."
- env:
API_KEY: ${{ secrets.API_KEY }} # secret do ambiente staging
deploy-production:
runs-on: ubuntu-latest
environment: production # faz referência ao ambiente production (pode exigir aprovação)
steps:
- run: echo "Deploying to production..."
- env:
API_KEY: ${{ secrets.API_KEY }} # secret do ambiente production
Nessa configuração, deploy-staging só acessa os secrets do ambiente staging, enquanto deploy-production só acessa os de production. Um não interfere no outro.
Além disso, Environment oferece regras de proteção. O ambiente de production, por exemplo, pode exigir aprovação manual antes da execução. Isso é especialmente útil para equipes.
Organization Secrets: compartilhamento e gestão centralizada para equipes
Se sua equipe tem dezenas de repositórios e todos precisam do mesmo AWS_ACCESS_KEY, copiar a configuração para cada um e depois repetir todas as alterações é um trabalho enorme.
Organization Secrets resolvem esse problema. Você configura o secret uma vez no nível da organização e todos os repositórios autorizados podem usá-lo. Também pode controlar o escopo do acesso: todos os repositórios ou apenas uma lista específica.
# No workflow do repositório, o uso é igual ao de repository secrets
steps:
- env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
Qual nível escolher?
Em resumo:
| Cenário | Nível recomendado |
|---|---|
| Projeto pessoal, ambiente único | Repository Secrets |
| Implantação em vários ambientes (staging/production) | Environment Secrets |
| Vários repositórios de uma equipe, credenciais compartilhadas | Organization Secrets |
Pela minha experiência, muitos projetos começam com Repository Secrets e só migram para Environment Secrets quando surge a necessidade de vários ambientes. A migração não custa muito, mas ainda assim vale planejar antes.
2. Boas práticas de segurança para secrets: oito regras fundamentais
Até aqui vimos onde armazenar os secrets. Agora vamos falar de como usá-los. Reuni oito regras aprendidas na prática, cada uma delas acompanhada de alguma lição.
1. Padronize os nomes
Use letras maiúsculas e separe as palavras com sublinhado, como AWS_ACCESS_KEY_ID. Evite minúsculas e camelCase. O motivo é simples: assim fica evidente que se trata de um secret, sem confusão com variáveis comuns.
2. Não armazene um JSON inteiro em um único secret
Esta é uma armadilha clássica. Algumas pessoas colocam o arquivo de configuração inteiro em um secret:
{"api_key": "xxx", "db_url": "yyy", "token": "zzz"}
Depois, usam fromJson para analisá-lo no workflow. O problema é que, se esse secret vazar, todas as informações confidenciais vazam de uma vez. O correto é armazenar cada valor em um secret separado.
3. Passe os secrets explicitamente, sem inseri-los diretamente no comando
# Forma incorreta ❌
- run: my-cli --token ${{ secrets.MY_TOKEN }}
# Forma correta ✅
- env:
MY_TOKEN: ${{ secrets.MY_TOKEN }}
run: my-cli --token $MY_TOKEN
Por quê? Uma pesquisa da GitGuardian mostra que outros processos na mesma máquina podem ver argumentos de linha de comando por meio de ps x -w. Variáveis de ambiente são relativamente mais seguras.
4. Faça a rotação regularmente
Troque os secrets a cada 30 a 90 dias. Eu sei que a rotação dá trabalho, mas é muito menos onerosa que remediar um vazamento. A equipe da Blacksmith recomenda combinar serviços de nuvem, como AWS e GCP, com OIDC para dispensar completamente essa etapa.
5. Fixe actions por SHA
Esta é a primeira linha de defesa contra ataques à cadeia de suprimentos.
# Forma incorreta ❌
- uses: tj-actions/changed-files@v45
# Forma correta ✅
- uses: tj-actions/changed-files@b827595e0a7e97537d7c7a2f458b5a8e6d5c8e39
Use o commit SHA, não a tag de versão. Um invasor pode alterar uma tag; o SHA é imutável.
6. Conceda apenas o menor privilégio ao GITHUB_TOKEN
O GitHub fornece automaticamente um GITHUB_TOKEN para cada workflow. As permissões padrão são amplas demais: ele pode escrever código e alterar issues. Recomendo defini-lo como read-only no workflow ou nas configurações do repositório:
permissions:
contents: read
7. Confirme que os secrets estão ocultos corretamente nos logs
O GitHub substitui automaticamente ${{ secrets.XXX }} por *** nos logs. Porém, se você escrever:
- run: echo "Token is $MY_TOKEN"
o valor real do token aparecerá no log. Teste o workflow e confirme que nada foi exposto por acidente.
8. Registre valores confidenciais derivados
Se o workflow gerar um novo valor confidencial a partir de um secret, como um JWT criado com uma API key, registre também esse novo valor como secret em vez de apenas mantê-lo na memória.
Essas oito regras não são apenas teoria. Depois do incidente com o tj-actions, a equipe da StepSecurity auditou milhares de repositórios públicos e constatou que uma parcela considerável violava essas regras. Corrigir os problemas não é difícil, mas exige algum tempo para revisar cada item.
3. OIDC: autenticação de implantação na nuvem sem chaves
Duas das regras anteriores mencionam a rotação periódica de secrets. Para ser sincero, essa tarefa é incômoda: a cada vez é preciso alterar manualmente o console da AWS, atualizar os secrets do GitHub e avisar a equipe.
O OIDC (OpenID Connect) oferece uma saída: não armazenar secrets.
Como o OIDC funciona?
No método tradicional, você cria um usuário IAM na AWS, gera uma access key e armazena a chave nos GitHub secrets. Sempre que o workflow é executado, ele usa essa chave estática para solicitar recursos da AWS.
Com OIDC, o GitHub atua como provedor de identidade e comprova à AWS que o workflow vem do repositório eastondev/my-repo. Após a validação, a AWS emite um token JWT de curta duração, válido por alguns minutos ou horas. O workflow usa o token temporário para concluir a tarefa, e ele perde a validade automaticamente depois de expirar.
Não há credenciais de longo prazo para armazenar, nenhuma rotação é necessária e não existe risco de vazamento dessas credenciais.
Exemplo de configuração do OIDC na AWS
O processo tem duas partes: configurar a relação de confiança na AWS e solicitar o token no GitHub.
Na AWS (operações no console):
- Crie um IAM Identity Provider e defina a URL como
https://token.actions.githubusercontent.com - Crie uma IAM Role e restrinja a política de confiança ao seu repositório:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:eastondev/my-repo:ref:refs/heads/main"
}
}
}]
}
Essa configuração determina que somente a branch main do repositório eastondev/my-repo pode assumir essa role.
No GitHub (configuração do workflow):
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # obrigatório: solicita o token OIDC
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole
aws-region: us-east-1
- run: aws s3 sync ./dist s3://my-bucket
Observe que não há nenhum secrets.AWS_ACCESS_KEY: a autenticação usa a role diretamente.
GCP e Azure
As três principais plataformas de nuvem oferecem suporte a OIDC, com configurações semelhantes:
| Plataforma de nuvem | GitHub Action | Termos para buscar na documentação oficial |
|---|---|---|
| AWS | aws-actions/configure-aws-credentials | OIDC federation |
| GCP | google-github-actions/auth | Workload Identity Federation |
| Azure | azure/login | Federated Identity Credentials |
Um benefício inesperado do OIDC
Segundo testes do johal.in, a latência da autenticação com OIDC é cerca de 87% menor que no método tradicional com secrets. O motivo é simples: não é necessário ler as credenciais pela API de secrets do GitHub; o token temporário é obtido diretamente pela troca do JWT local.
Na minha experiência, OIDC é a melhor opção para implantações na nuvem. A única barreira é uma configuração um pouco mais complexa, mas, depois de concluí-la uma vez, você não precisa mais se preocupar.
4. Proteção contra ataques à cadeia de suprimentos: análise do incidente com o tj-actions
Voltando ao incidente com tj-actions/changed-files mencionado no início: como ele aconteceu e o que podemos aprender?
O que aconteceu
Em março de 2025, invasores obtiveram acesso de maintainer ao repositório do tj-actions. O método exato ainda estava sob investigação e poderia ter envolvido vazamento de credenciais ou comprometimento de conta. Eles inseriram um script malicioso no código da versão v45. Durante a execução do workflow, o script lia silenciosamente todas as variáveis de ambiente e secrets e os enviava a um servidor controlado pelos invasores.
Segundo a análise da Semgrep, todos os workflows que usavam tj-actions/changed-files@v45 foram afetados. Isso valia tanto para GitHub secrets quanto para OIDC: qualquer variável de ambiente acessível à action poderia ser roubada.
Um relatório da Unit 42, da Palo Alto, apontou que mais de 23.000 repositórios usavam essa action, incluindo forks de alguns projetos conhecidos.
Lições
O incidente expôs vários problemas:
- Tags de versão de actions não são confiáveis: um invasor pode fazer uma tag apontar para um commit malicioso
- Actions de terceiros podem acessar seus secrets: se forem comprometidas, todos os secrets acessíveis ficam expostos
- Logs de execuções anteriores podem vazar informações confidenciais: mesmo após a correção, execuções passadas ainda podem conter vestígios
Sua lista de verificação
Se você já usou tj-actions/changed-files, verifique cada item:
□ Verifique os logs do workflow e confirme que nenhum secret vazou na saída
□ Faça a rotação de todos os secrets que possam ter sido expostos (API keys, tokens etc.)
□ Fixe a action por commit SHA, não por tag de versão
□ Audite a origem dos maintainers de outras actions de terceiros
□ Considere usar Dependabot ou Renovate para automatizar a verificação de versões das actions
Para projetos que ainda não foram comprometidos, fixar por SHA é a medida de proteção mais importante. Embora o SHA seja menos legível que um número de versão, essa é a única forma de impedir alterações de tag.
Além disso, o GitHub mencionou uma direção importante em seu roteiro de segurança para 2026: separar as permissões para contribuir com código das permissões para gerenciar credenciais. No futuro, talvez sejam introduzidos controles de acesso mais granulares, permitindo que actions de terceiros acessem somente os secrets necessários, em vez de todos. É uma boa notícia, mas, por enquanto, ainda precisamos proteger nossos próprios limites.
Conclusão
Gerenciar GitHub Actions Secrets não é um problema técnico complexo, mas uma prática de segurança que exige atenção contínua. Em resumo:
Como escolher entre os três níveis: use Repository Secrets em projetos pessoais, migre para Environment Secrets em implantações com vários ambientes e use Organization Secrets para compartilhamento em equipe.
Como aplicar as regras de segurança: fixar por SHA, passar secrets explicitamente e fazer rotação periódica são as três mais importantes.
Como autenticar implantações na nuvem: OIDC é a primeira opção, pois dispensa o armazenamento e a rotação de secrets.
Como evitar ataques à cadeia de suprimentos: limite o acesso de actions de terceiros aos secrets e audite a origem dos maintainers.
Depois de aprender com o incidente do tj-actions, passei a fixar todas as actions por SHA, usar OIDC em todas as implantações na nuvem e auditar secrets uma vez por mês. Levei cerca de duas semanas para estabelecer esse processo, mas o custo de manutenção desde então tem sido baixo.
Se você ainda não começou essas verificações, vale executar hoje mesmo a lista deste artigo. Prevenir custa muito menos que remediar um incidente.
Configurar uma implantação sem chaves no GitHub Actions com OIDC
Configure a autenticação OIDC para a AWS e faça implantações seguras sem armazenar credenciais estáticas
⏱️ Estimated time: 30 min
- 1
Step 1: Criar um IAM Identity Provider
Crie um provedor de identidade no console do AWS IAM:
• Provider URL: https://token.actions.githubusercontent.com
• Audience: sts.amazonaws.com
• Anote o Provider ARN gerado - 2
Step 2: Criar uma IAM Role e configurar a política de confiança
Crie uma IAM Role e restrinja a política de confiança ao seu repositório do GitHub:
```json
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:OWNER/REPO:ref:refs/heads/main"
}
}
}]
}
```
• Substitua ACCOUNT_ID pelo ID da sua conta AWS
• Substitua OWNER/REPO pelo seu repositório do GitHub
• Remova :ref:refs/heads/main se quiser permitir todas as branches - 3
Step 3: Configurar o workflow para usar OIDC
Solicite um token OIDC no workflow do GitHub Actions:
```yaml
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # obrigatório: solicita o token OIDC
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::ACCOUNT_ID:role/GitHubActionsRole
aws-region: us-east-1
- run: aws s3 sync ./dist s3://my-bucket
```
• id-token: write é uma declaração de permissão obrigatória
• Não é necessário configurar secrets.AWS_ACCESS_KEY_ID - 4
Step 4: Testar e validar
Execute o workflow para validar a configuração OIDC:
• Confira os logs do workflow e confirme que a autenticação foi bem-sucedida
• Verifique se role-to-assume está correto
• Confirme que os recursos da AWS podem ser acessados sem credenciais estáticas
• Teste a operação desejada, como sincronização com S3 ou envio para o ECR
FAQ
Qual é a diferença entre Repository Secrets e Environment Secrets?
Como usar secrets com segurança em um workflow?
• Passagem explícita: injete pelo campo env em vez de usar ${{ secrets.XXX }} diretamente na linha de comando
• Menor privilégio: passe o secret somente ao step que precisa dele; ao usar GITHUB_TOKEN, defina permissions: contents: read
• Rotação periódica: troque os secrets a cada 30 a 90 dias; em implantações na nuvem, use OIDC para dispensar a rotação
O que é OIDC e por que ele é mais seguro que secrets tradicionais?
Como evitar ataques à cadeia de suprimentos, como o incidente do tj-actions?
• Fixar actions por commit SHA, não por tag de versão
• Limitar a passagem de secrets somente a actions confiáveis
• Auditar a origem dos maintainers de actions de terceiros
• Executar Dependabot ou Renovate regularmente para verificar atualizações de versão
Os secrets podem vazar nos logs?
Para quais situações Organization Secrets são indicados?
11 min de leitura · Publicado em: 18 abr 2026 · Atualizado em: 8 set 2026
Guia completo GitHub Actions
Se você chegou pela busca, o caminho mais rápido é ir para o post anterior ou próximo desta série.
Anterior
Estratégias de deploy com GitHub Actions: de VPS a plataformas de nuvem
Compare deploy em VPS por SSH, plataformas como Vercel, Cloudflare e Netlify e arquitetura híbrida com GitHub Actions, workflows e solução de falhas comuns.
Parte 5 de 6
Próximo
Este é o post mais recente da série até agora.



Comentários
Entre com GitHub para comentar