Gerenciamento seguro de segredos no GitHub Actions

Última atualização: 12/01/2025
  • Os segredos do GitHub Actions são variáveis ​​de ambiente criptografadas e com escopo definido, que devem ser cuidadosamente delimitadas nos níveis de repositório, ambiente e organização.
  • A segurança depende do princípio do menor privilégio, evitando a exposição de logs, rotacionando e auditando segredos e isolando ambientes de produção sensíveis.
  • Os riscos decorrentes de injeção de scripts, ações de terceiros e executores auto-hospedados exigem fixação de privilégios, revisão de código e políticas rigorosas de executores e permissões.
  • O OpenID Connect e os gerenciadores de segredos externos ajudam a substituir credenciais de longa duração por tokens de curta duração e fluxos de trabalho de segredos centralizados e auditáveis.

Gerenciamento de segredos do GitHub Actions

Gerenciar segredos no GitHub Actions é um daqueles tópicos que parecem simples à primeira vista, mas rapidamente se transformam em uma preocupação crítica de segurança quando seus pipelines começam a interagir com produção, provedores de nuvem e serviços de terceiros. Seus fluxos de trabalho de CI/CD lidam rotineiramente com chaves de API, senhas de banco de dados, chaves SSH, tokens e muito mais, e cada um desses valores é um ponto de entrada potencial para um invasor se forem tratados de forma descuidada.

Neste guia, vamos explorar em detalhes como os segredos funcionam no GitHub Actions, como configurá-los nos níveis de repositório, ambiente e organização, como proteger os fluxos de trabalho contra vazamentos e ataques à cadeia de suprimentos e quando faz sentido recorrer a gerenciadores de segredos externos. A ideia é fornecer uma visão geral prática e focada em segurança para que você possa manter seus pipelines funcionando rapidamente. e seguro sem transformar o trabalho diário em uma dor de cabeça.

O que são exatamente os segredos do GitHub Actions?

No GitHub Actions, um "segredo" é uma variável de ambiente criptografada cujo valor fica oculto da interface do usuário, dos registros e do conteúdo do repositório. Você o define uma vez (no nível do repositório, da organização ou do ambiente) e, em seguida, o referencia no YAML do seu fluxo de trabalho usando o secrets. contexto, para que seus pipelines possam usar valores sensíveis sem nunca os confirmar no código-fonte.

Nos bastidores, o GitHub criptografa os segredos usando criptografia forte (caixas seladas do Libsodium) antes mesmo de chegarem aos servidores do GitHub, e os valores só são descriptografados em tempo de execução no executor do fluxo de trabalho. Uma vez criados, os segredos são imutáveis ​​na interface do usuário: você pode sobrescrevê-los, mas não pode lê-los novamente, e quando aparecem nos registros, são automaticamente mascarados com *** qualquer lugar possível.

Este modelo apresenta algumas restrições de design importantes que você precisa conhecer: os segredos não podem ser recuperados pela interface do usuário ou pela API, são ocultados dos registros e residem em um escopo específico: Repositório, ambiente dentro de um repositório ou organização. Escolher o escopo correto é a primeira grande decisão para uma estratégia de segurança eficaz.

Escopos secretos no GitHub Actions

Segredos de repositório, ambiente e organização

O GitHub oferece três camadas principais para segredos: segredos do repositório, segredos do ambiente e segredos da organização, cada uma com seus próprios casos de uso e regras de precedência. Compreender como eles interagem ajuda a evitar conflitos e a manter os valores sensíveis onde devem estar.

Segredos de nível de repositório

Os segredos do repositório estão vinculados a um único repositório e estão disponíveis para todos os fluxos de trabalho nesse repositório. São perfeitos para valores específicos do projeto, como a chave da API de um serviço, uma senha de implantação ou um token de webhook usado apenas por esse repositório.

Para criar um segredo de repositório a partir da interface do usuário, acesse o repositório desejado, abra “Configurações” → “Segredos e variáveis” → “Ações” e clique em “Novo segredo de repositório”. Você atribui a ele um nome em maiúsculas com sublinhados (por exemplo, PAYMENTS_API_KEY), cole o valor secreto e salve; a partir desse momento, os fluxos de trabalho poderão acessá-lo como ${{ secrets.PAYMENTS_API_KEY }}.

Qualquer pessoa com acesso de escrita ao repositório pode referenciar esses segredos nos fluxos de trabalho, portanto, as permissões no próprio repositório passam a fazer parte da sua estratégia de segurança. Se você conceder acesso de escrita a muitos usuários de forma leviana, estará implicitamente concedendo a eles acesso para usar todos os segredos do repositório na automação.

Segredos específicos do ambiente

Os segredos de ambiente ficam um nível abaixo dos segredos do repositório e permitem definir valores diferentes para cada ambiente, como por exemplo: dev, staging, ou production. Eles estão associados a um ambiente nomeado e podem ser protegidos com regras como revisores obrigatórios ou temporizadores de espera antes que uma tarefa possa ser executada neles.

Você configura isso acessando “Configurações” → “Ambientes”, criando ou selecionando um ambiente e, em seguida, adicionando segredos dentro da configuração desse ambiente. Os nomes secretos ainda usam o secrets. contexto (por exemplo secrets.PROD_DB_PASSWORD), mas os valores só ficam disponíveis para tarefas que são executadas explicitamente nesse ambiente.

Um detalhe fundamental é que os segredos do ambiente têm precedência sobre os segredos do repositório quando compartilham o mesmo nome. Isso significa que você pode definir DB_PASSWORD no nível do repositório para usos locais/de teste e, em seguida, ter um diferente DB_PASSWORD como um segredo de ambiente para produção que tem precedência em tarefas de produção, sem alterar a sintaxe do fluxo de trabalho.

Os ambientes também permitem regras de proteção como "revisores obrigatórios" ou "somente de determinadas ramificações", o que é incrivelmente útil para restringir o acesso aos seus segredos mais sensíveis. Por exemplo, um ambiente de produção pode exigir aprovação da equipe de DevOps antes que qualquer tarefa que utilize seus segredos possa ser executada.

Segredos de toda a organização

Os segredos organizacionais são compartilhados entre vários repositórios em uma organização e são ideais para credenciais amplamente reutilizadas, como um webhook compartilhado do Slack ou um token de API de métricas central. Elas reduzem a duplicação e facilitam a rotação, pois você atualiza o segredo uma única vez e todos os repositórios que o utilizam captam o novo valor.

Os administradores criam esses segredos na seção “Configurações” → “Segredos e variáveis” → “Ações” da organização, clicando em “Novo segredo da organização” e, em seguida, escolhendo quais repositórios podem acessar esse segredo. Você pode permitir todos os repositórios atuais e futuros ou restringir rigorosamente a um subconjunto específico.

Existe uma ordem de precedência que você deve ter em mente: segredo da organização < segredo do repositório < segredo do ambiente quando os nomes entrarem em conflito. Em outras palavras, um segredo de ambiente prevalece sobre um segredo de repositório, que por sua vez prevalece sobre um segredo de organização, caso todos compartilhem a mesma chave.

Como os segredos se comportam em tempo de execução

Uma vez definidos, os segredos ficam disponíveis para os trabalhos em tempo de execução por meio do secrets O contexto e são injetados como variáveis ​​de ambiente quando referenciados. Eles não são amplamente exportados para todas as etapas por padrão; você os configura explicitamente em seu projeto. env: bloqueia ou os passa para ações que aceitam segredos como entrada.

O GitHub também oferece um recurso especial GITHUB_TOKEN por execução de fluxo de trabalho, que não é um segredo definido manualmente, mas se comporta como tal e é frequentemente usado para chamadas de API ou operações de repositório. Você pode (e deve) ajustar as permissões detalhadas deste token usando o permissions: O bloco deve ter o escopo mínimo necessário para cada tarefa.

Para reduzir a exposição acidental, o GitHub mascara qualquer valor que corresponda a um segredo registrado nos logs de fluxo de trabalho, substituindo-o por ***. Essa máscara é aplicada no lado do executor e geralmente funciona bem para strings brutas, mas pressupõe que o valor secreto exato apareça na saída. Se você transformar o segredo (por exemplo, codificá-lo em base64 ou incorporá-lo em JSON estruturado), a máscara pode não conseguir detectá-lo.

Como o mascaramento é feito da melhor maneira possível e não tem garantia matemática, você deve projetar fluxos de trabalho para evitar a impressão de segredos ou seus derivados nos registros, e usar comandos de mascaramento para valores adicionais gerados em tempo de execução. Considere os registros como potencialmente visíveis para mais pessoas do que você imagina e assuma que qualquer coisa impressa pode vazar.

Uso prático: referenciar segredos em fluxos de trabalho.

Na maioria das vezes, você usará segredos mapeando-os em variáveis ​​de ambiente em uma etapa ou tarefa específica e, em seguida, permitindo que seus scripts ou ferramentas leiam o ambiente. Um padrão clássico tem esta aparência:


– nome: Implantar na API
ambiente:
API_KEY: ${{ secrets.PROD_API_KEY }}
executar: |
curl -H “Authorization: Bearer $API_KEY” https://api.example.com/deploy

Você também pode gravar um segredo em um arquivo no executor, que permanecerá seguro enquanto o arquivo permanecer dentro do espaço de trabalho efêmero da tarefa e não for confirmado ou carregado como um artefato. Por exemplo, armazenar uma chave SSH:


– nome: Gravar chave SSH em arquivo
shell: bash
ambiente:
SSH_KEY: ${{ secrets.SSH_KEY }}
executar: |
echo “$SSH_KEY” > chave
chmod 600 chave

Nos registros, você verá apenas o comando shell propriamente dito (com $SSH_KEY), mas não o valor secreto em si, que será redigido ou ocultado. Como os runners hospedados no GitHub são destruídos após a conclusão da tarefa, esse arquivo temporário desaparece com a VM; em runners auto-hospedados, você deve ser muito mais rigoroso com a limpeza.

Melhores práticas de segurança para segredos no GitHub Actions

Utilizar apenas a interface de segredos não é suficiente; você precisa de um conjunto de hábitos e medidas de segurança para minimizar as consequências caso algo dê errado. O GitHub oferece muitas opções, mas cabe a você ajustá-las corretamente.

Aplique o princípio do privilégio mínimo

Cada segredo e cada token deve conceder apenas as permissões absolutamente necessárias para uma determinada tarefa. Para serviços externos, crie credenciais dedicadas com permissões específicas (por exemplo, "somente implantação" ou "métricas somente leitura") em vez de reutilizar chaves de administrador completo.

O mesmo princípio se aplica ao embutido. GITHUB_TOKEN; defina as permissões padrão para o mínimo necessário (frequentemente contents: read) e então aumentar as permissões apenas em tarefas específicas que precisam de mais. Você configura isso com um permissions: seção no nível do fluxo de trabalho ou da tarefa, para que uma tarefa comprometida não possa realizar gravações arbitrárias silenciosamente.

Evite imprimir ou codificar segredos em registros.

Os segredos nunca devem ser codificados diretamente nos arquivos de fluxo de trabalho nem impressos em texto simples para facilitar a depuração. Se você tiver outros valores confidenciais que não estejam registrados como segredos do GitHub (por exemplo, um token gerado em tempo de execução), ainda poderá solicitar ao executor que os trate como segredos usando a seguinte sintaxe de comando:


echo “::add-mask::$GENERATED_TOKEN”

Objetos estruturados como JSON, XML ou grandes documentos YAML são especialmente perigosos como segredos, porque o mecanismo de mascaramento do GitHub depende da correspondência exata de strings. Se você inserir vários valores sensíveis em uma única string JSON grande e usá-la como um único segredo, pequenas alterações de formatação podem fazer com que a máscara falhe; em vez disso, defina segredos individuais para cada campo sensível.

Sempre revise os registros ao testar fluxos de trabalho, prestando atenção especial às mensagens de erro e aos rastreamentos de pilha. Algumas ferramentas enviam comandos e parâmetros para stderr sem problemas, o que pode incluir acidentalmente valores secretos, a menos que você evite explicitamente esse padrão.

Rotacione e audite os segredos regularmente.

A rotação de senhas secretas é tediosa, mas indispensável se você se preocupa com a segurança; manter as credenciais inalteradas por anos aumenta a janela de oportunidade para os invasores. Uma prática básica razoável é rotacionar mensalmente os segredos de produção mais críticos, os de alto risco a cada dois meses e todos os demais pelo menos trimestralmente.

Você pode automatizar parte disso usando a API REST do GitHub para segredos, que permite obter a chave pública de um repositório ou organização e carregar novos valores criptografados. Isso é particularmente útil para grandes organizações com muitos repositórios que compartilham contas de serviço e precisam rotacioná-las rapidamente em resposta a incidentes.

A auditoria é igualmente importante: revise periodicamente a lista de segredos configurados e exclua aqueles que não são mais usados, e utilize os logs de segurança/auditoria do GitHub para rastrear eventos como este. org.update_actions_secret. Dessa forma, você saberá quem alterou o quê e quando, podendo correlacionar mudanças suspeitas com outras atividades.

Utilize a separação baseada no ambiente.

Os segredos de ambiente são uma das maneiras mais fáceis de reforçar a segurança dos seus pipelines, pois permitem isolar valores altamente sensíveis (como credenciais de banco de dados de produção) por trás de aprovações explícitas. Você pode exigir revisores humanos, limitar quais branches podem ser implantadas e até mesmo adicionar temporizadores de espera antes do início de uma implantação.

Um padrão comum é ter um staging ambiente com proteções leves e um production Ambiente com regras mais rígidas e segredos bem definidos. Os fluxos de trabalho definem tarefas direcionadas a cada ambiente, garantindo que os segredos de produção nunca sejam usados ​​acidentalmente em tarefas de teste.

Escolha convenções de nomenclatura claras.

Dar bons nomes aos segredos evita frustrações com palpites e confusões perigosas. Em vez de nomes genéricos como API_KEY, codifique o serviço e o ambiente no nome, por exemplo STRIPE_PROD_API_KEY or AWS_STAGING_DEPLOY_ROLE_ARN.

Equipes que lidam com muitos serviços frequentemente adotam um padrão como este: <SERVICE>_<ENV>_<PURPOSE>. Então você pode ter SLACK_PROD_ALERTS_WEBHOOK, GCP_DEV_BUILD_SERVICE_ACCOUNT e DB_STAGING_PASSWORDIsso torna muito mais óbvio qual segredo deve ser usado em cada tarefa.

Proteção contra injeção de scripts e riscos de terceiros

Os segredos não estão apenas em risco devido a configurações incorretas; eles também são alvos tentadores para injeção de scripts em fluxos de trabalho e ações maliciosas ou comprometidas de terceiros. Um único passo em falso pode expor todos os segredos acessíveis à tarefa, caso você não seja cuidadoso.

Mitigando a injeção de scripts em etapas embutidas

Ao interpolar dados não confiáveis ​​(como títulos de solicitações de pull, nomes de branches ou comentários de issues) diretamente em scripts de shell, você abre caminho para injeção de malware. Por exemplo, o título de um PR (Pull Request) poderia ser criado para escapar de um comando e executar código shell arbitrário em seu executor.

A abordagem mais segura é lidar com a lógica complexa em ações JavaScript/TypeScript próprias ou bem auditadas e passar valores não confiáveis ​​como entradas em vez de incorporá-los diretamente no shell. Nesse modelo, a ação recebe strings como argumentos e as processa sem gerar scripts de shell que possam ser sequestrados.

Se precisar usar o shell embutido, armazene primeiro os valores não confiáveis ​​em variáveis ​​de ambiente e, em seguida, faça referência a essas variáveis, de preferência entre aspas duplas. Dessa forma, o valor é tratado como dado e não como parte da estrutura do script, tornando as tentativas de injeção muito menos prováveis ​​de sucesso.

Fixar e analisar ações de terceiros

Cada ação de terceiros que você incorpora ao seu fluxo de trabalho é executada com acesso ao ambiente e aos segredos da tarefa, portanto, você deve tratá-las como dependências de código que exigem análise criteriosa. Uma ação maliciosa ou comprometida pode ler segredos e enviá-los a um atacante com uma única chamada HTTP.

A melhor prática é fixar ações pelo SHA completo do commit, em vez de apenas por tags ou branches, porque as tags podem ser movidas ou sobrescritas. Um SHA refere-se a uma versão exata do código, tornando muito mais difícil para um invasor injetar silenciosamente um novo comportamento sem que você atualize o fluxo de trabalho.

Antes de usar uma ação, examine seu código-fonte (ou pelo menos uma análise de segurança) para garantir que ela lide com segredos de forma responsável e não os registre ou os envie para endpoints desconhecidos. Se você utiliza ações do marketplace, verifique o editor sempre que possível e confie no Dependabot para alertá-lo sobre vulnerabilidades e atualizações.

Corridas organizadas versus corridas auto-organizadas e exposição secreta

O local onde seus fluxos de trabalho são executados tem um enorme impacto na segurança com que os segredos são tratados. Os runners hospedados no GitHub e os runners auto-hospedados comportam-se de maneira muito diferente em termos de isolamento e persistência.

Os executores hospedados no GitHub criam novas máquinas virtuais para cada tarefa, executam seu fluxo de trabalho e, em seguida, as desativam. Isso proporciona um ambiente limpo sempre e garante que todos os arquivos ou variáveis ​​de ambiente (incluindo segredos gravados em disco) sejam destruídos assim que a tarefa for concluída.

Em contraste, os runners auto-hospedados são máquinas de longa duração que você gerencia, o que significa que qualquer código com acesso a segredos pode potencialmente persistir ou exfiltrá-los além da duração de uma única tarefa. Em repositórios públicos, os executores de código auto-hospedados são particularmente arriscados porque colaboradores não confiáveis ​​podem abrir solicitações de pull que acionam fluxos de trabalho.

Se você usar executores de teste auto-hospedados, isole-os por nível de sensibilidade, restrinja quais repositórios podem usar quais executores e seja extremamente cauteloso com o que mais reside nessas máquinas (chaves SSH, credenciais de nuvem, acesso à rede para serviços internos e assim por diante). Algumas organizações usam executores auto-hospedados "just-in-time" (JIT) que são criados via API para uma única tarefa e depois destruídos, mas mesmo assim é preciso garantir que as tarefas não compartilhem o mesmo executor inesperadamente.

Utilizando OpenID Connect (OIDC) em vez de segredos de longa duração na nuvem.

Uma das maiores conquistas em termos de segurança de dados no GitHub Actions é a substituição de chaves de acesso à nuvem de longa duração por credenciais de curta duração via OpenID Connect. Em vez de armazenar chaves da AWS, Azure ou GCP como segredos, seus fluxos de trabalho solicitam tokens temporários do provedor de nuvem usando o GitHub como provedor de identidade.

O fluxo funciona mais ou menos assim: a tarefa solicita um JWT assinado do endpoint OIDC do GitHub, seu provedor de nuvem valida esse token e o troca por credenciais de curta duração, e o fluxo de trabalho usa essas credenciais durante a execução da tarefa. Nenhum segredo estático precisa ficar armazenado no GitHub.

Por exemplo, com a AWS, você configura uma função do IAM que confia no provedor OIDC do GitHub e restringe quais repositórios/branches podem assumir essa função. Em seguida, no seu fluxo de trabalho, você usa uma ação como aws-actions/configure-aws-credentials Com permissões OIDC habilitadas para obter credenciais dinamicamente.

Essa abordagem oferece diversas vantagens: não há nada para rotacionar dentro do GitHub, os tokens têm duração automaticamente curta, o acesso é restrito e você obtém registros de auditoria completos na nuvem, rastreando cada atribuição de função. Para ambientes de alta segurança, o OIDC deve ser o padrão, onde for compatível.

Ferramentas nativas e gerenciadores de segredos externos

Os segredos integrados do GitHub são ótimos para muitos cenários, mas em algum momento você pode querer um gerenciador de segredos mais centralizado e rico em recursos, que abranja várias plataformas e ambientes. Ferramentas como HashiCorp Vault, Infisical ou Doppler são frequentemente usadas em conjunto com o GitHub Actions para essa finalidade.

Esses sistemas podem lidar com segredos dinâmicos (por exemplo, gerar usuários de banco de dados de curta duração), políticas avançadas de controle de acesso, registros de auditoria detalhados e fluxos de trabalho de rotação que vão além do que o GitHub sozinho oferece. O GitHub Actions autentica-se nesses gerenciadores (geralmente via OIDC ou outro método de autenticação), busca os segredos necessários em tempo de execução e os utiliza sem nunca armazenar credenciais de longo prazo no repositório.

Existem também ações da comunidade e plugins projetados para extrair segredos de gerentes externos diretamente para os fluxos de trabalho. Ao utilizá-los, as mesmas recomendações se aplicam: revise o código-fonte da ação, vincule-a a um SHA de commit e limite os privilégios concedidos ao token ou à função que ela usa para acessar o sistema externo.

O gerenciamento seguro de segredos no GitHub Actions significa escolher o escopo correto para cada segredo, aplicar o princípio do menor privilégio, usar ambientes e OIDC quando apropriado, tratar logs e ações de terceiros como potenciais superfícies de ataque e recorrer a gerenciadores de segredos externos quando a escala ou os requisitos de conformidade assim o exigirem. Com essas práticas implementadas, seus pipelines de CI/CD podem permanecer flexíveis e rápidos, reduzindo drasticamente as chances de que um token mal posicionado ou um fluxo de trabalho mal revisado se transforme em um incidente de grandes proporções.

Artigos relacionados: