Como auditar as dependências do seu projeto Ruby e rotacionar a chave do RubyGems?

Fluxo para auditar dependências Ruby e rotacionar chave do RubyGems
Resposta rápida

Auditar dependências Ruby virou rotina obrigatória depois da campanha de publicação em massa no RubyGems, com mais de 500 pacotes maliciosos removidos e um advisory de vazamento de chave de API legada em 22 de julho de 2026. O roteiro é curto: rodar bundle-audit update e bundle-audit check contra o Gemfile.lock, olhar o diff das gems novas, travar versões com lockfile, frozen e cooldown, e rotacionar a credencial com gem signout, gem update --system, nova chave no RubyGems.org e gem signin. Depois disso, revisar owners e trusted publishers, porque isso persiste mesmo com a chave revogada

Fala aí, beleza?

Centenas de pacotes despejados de uma vez no RubyGems, nome e e-mail com cara de coisa gerada em série, código com jeitão de LLM e, meses depois, um advisory de vazamento de chave de API legada

Se você tem um Gemfile.lock vivo em produção, isso deixou de ser fofoca de timeline

Este post não é comentário sobre o relatório, é roteiro de trabalho: auditar dependências Ruby que entraram recentemente no lockfile, travar versões e trocar a credencial antiga

No fim você tem dois resultados verificáveis na mão: a lista de gems novas revisada e a chave velha invalidada

Bora?

O que aconteceu no RubyGems e por que isso te obriga a auditar

O caso veio a público em 12 de maio de 2026, quando Maciej Mensfeld, do time de segurança do RubyGems, avisou que havia um ataque malicioso em andamento e que os cadastros estavam pausados

Centenas de pacotes envolvidos

O RubyGems.org depois publicou o posicionamento oficial da campanha de maio: contas responsáveis bloqueadas e removidas, mais de 500 pacotes maliciosos removidos (yank) e cadastros reabertos em 16 de maio de 2026

A mesma campanha de publicação em massa já tinha sido documentada antes pela Socket com o nome GemStuffer

E de onde veio a história dos agentes?

Em 11 e 12 de setembro de 2026, Spencer Kitts, Thomas Larsen e Sydney Von Arx publicaram em rubyhack.ai um relatório sustentando que um enxame de agentes da OpenAI esteve por trás do ataque ao RubyGems

São os mesmos autores do relatório sobre o ataque de agentes a wikis desativados

Aí vem a ressalva importante, e ela não é detalhe: o RubyGems.org afirma que, com base nas evidências disponíveis a eles, não é possível determinar se os pacotes foram criados ou publicados por agentes de IA

Formação Claude Code
Formação Recomendada

Formação Claude Code

Domine Claude Code do absoluto zero até o avançado

  • 120 aulas
  • 4 projetos
  • 9h 45min

Ou seja: os pesquisadores atribuem, o repositório não confirma a atribuição

A OpenAI, em posicionamento citado pela imprensa, descreve o uso como agentes acessando a internet pela plataforma para executar tarefas benignas e recuperar informação pública

Isso é declaração citada, não fato fechado, e eu prefiro deixar isso explícito a te vender certeza que ninguém tem

Os padrões que os pacotes deixaram para trás:

O que ficou documentado sobre os pacotes é justamente o que serve de checklist pra você:

  • muitos traziam oai no nome, no campo de autor ou no endereço de e-mail falso informado
  • os arquivos acessados usavam truques parecidos com os dos agentes que atacaram wikis, incluindo r.jina.ai
  • o código dos pacotes aparentava ter sido escrito por LLM
  • muitos exploravam o processo de build de documentação do RubyDoc.info para exfiltrar dados públicos de sites do governo do Reino Unido

E teve um agente que deixou o comentário no código, sem nenhum pudor:

# malicious crawler/exfil for Southwark Jan 2026 docs via rubydoc.info worker

Os pacotes também tentaram roubar chaves de API por um exploit que só foi corrigido mais de dois meses depois, e não está claro se essas tentativas tiveram sucesso

A lição prática independe de quem apertou o botão: agente que publica pacote, agente que roda script, agente mexendo nas suas pull requests, tudo isso é código de origem automática encostando no seu projeto

A régua é a mesma: revisar o que entrou

O que muda para quem tem gems publicadas ou só instala dependências

Dois perfis, dois níveis de urgência

Se você só consome gems:

Respira

Segundo o RubyGems.org, instalações de gems e publicações de usuários já existentes não foram afetadas durante a campanha, e os pacotes amplamente usados não foram atingidos

No caso do vazamento de chave legada, as gems já publicadas nunca estiveram em risco: releases existentes não podem ser reescritas e a instalação de gems não foi afetada

Mesmo assim a auditoria vale, porque o que te protege não é sorte, é saber o que entrou no lockfile

Se você publica gems:

Aí o buraco é outro

Em 22 de julho de 2026 o RubyGems.org publicou um advisory de segurança sobre vazamento de chave de API legada (GHSA-9j48-x3c3-mrp2), causado por configuração inadequada de cache

Como funcionava a falha?

O endpoint rubygems.org/api/v1/api_key autentica via HTTP Basic Auth e, a cada GET autenticado com sucesso, gera uma nova chave de API legada no corpo da resposta

Com o cache mal configurado, essa resposta podia ficar armazenada na borda da CDN (Fastly) e ser entregue a outro chamador por até uma hora

Um baita problema, né?

Estavam expostas as contas que fizeram sign-in no rubygems.org com cliente gem anterior à versão 3.2.0, ou que usavam chave legada por outro caminho

O advisory aponta que 18% dos sign-ins via gem signin vinham de versão afetada do cliente

Quem estivesse com uma chave vazada na mão poderia agir como o dono da conta: publicar nova versão, dar yank e se adicionar como owner

O RubyGems revisou os logs de acesso, não encontrou sinal de uso malicioso de chave legada e revogou todas as chaves legadas do RubyGems.org, pedindo que os donos verificassem as próprias gems

Como só as legadas foram revogadas, gem install e bundle install seguem funcionando (são anônimos)

O que quebra é o próximo gem push, gem yank ou gem owner feito com chave legada expirada: retorna HTTP 401

Se você tomou 401 do nada num release e xingou o CI, era isso 🙂

O que você precisa antes de começar a auditoria

Separa isso aqui antes de abrir o terminal:

  • acesso ao repositório com Gemfile e Gemfile.lock versionados
  • cliente gem na versão 3.2.0 ou mais nova, que você garante com gem update --system
  • o bundler-audit instalado, via gem install bundler-audit
  • acesso à sua conta do RubyGems.org com a senha em mãos, porque ela é pedida na tela de API Keys para confirmar identidade
  • se você publica e pensa em migrar o release para trusted publishing, acesso ao repositório no GitHub

Tome cuidado com o item da senha: muita gente hoje entra no RubyGems.org por sessão salva e não lembra a senha, aí trava no meio da rotação

Passo a passo: auditar as dependências novas do lockfile

A ideia aqui é simples: primeiro o que é conhecido e automatizável, depois o olho humano no que é novo

  1. Rode o scan do bundler-audit contra o lockfile

O bundler-audit é mantido no repositório rubysec/bundler-audit e faz verificação em nível de patch para o Bundler: ele checa versões vulneráveis de gems do seu Gemfile.lock contra o ruby-advisory-db e também detecta fontes inseguras (http:// e git://)

A checagem em si não precisa de conexão de rede

gem install bundler-audit
bundle-audit update
bundle-audit check

Se você quer atualizar a base de advisories e escanear numa tacada só:

bundle-audit check --update

O erro comum deste passo: achar que o bundle-audit limpo significa projeto limpo

Ele cobre vulnerabilidade conhecida e fonte insegura

Pacote novo recém publicado, sem advisory nenhum registrado, passa batido por definição, e era exatamente esse o formato do episódio de maio

  1. Isole o que entrou recentemente no lockfile

Aqui não tem mágica, é diff mesmo

git diff <commit-anterior> Gemfile.lock

O Gemfile.lock é o seu inventário honesto: ele lista o que realmente foi resolvido, incluindo as dependências transitivas que você nunca escreveu no Gemfile

Separe essa lista de gems novas numa nota, é ela que vai pro próximo passo

O erro comum deste passo: olhar só o Gemfile

O pacote suspeito raramente entra pela porta da frente, ele chega de carona como dependência de dependência

  1. Passe cada gem nova pelos padrões documentados do episódio

Pra cada item da sua lista, olhe os metadados e o código na fonte:

  • nome, campo author e e-mail de contato contêm oai?
  • o código faz requisição via r.jina.ai?
  • o código tem cara de escrito por LLM (comentário explicativo demais, estrutura genérica, função que não conversa com o propósito declarado da gem)?
  • tem algo mexendo no processo de build de documentação do RubyDoc.info?

Nenhum desses sinais é prova isolada, combinação deles é que acende a luz vermelha

O erro comum deste passo: confiar no README

README bonito é a parte mais barata de gerar, o que importa é o que o arquivo executa

  1. Veja o que está desatualizado antes de decidir o que mexer
bundle outdated

O bundle outdated lista as gems instaladas que têm versões mais novas disponíveis, e tem opções para listar apenas as versões permitidas pelos requisitos do seu Gemfile

O erro comum deste passo: sair atualizando tudo no susto logo depois de ler uma notícia de ataque de cadeia de suprimentos

Atualizar às cegas é justamente puxar versão fresquinha sem ninguém ter olhado ainda

Calma, o próximo bloco resolve isso…

Passo a passo: travar versões para reduzir a janela de risco

A janela perigosa num ataque de supply chain é o intervalo entre alguém comprometer uma conta e a versão maliciosa ser publicada

Seu trabalho é não estar nessa janela

  1. Commite e honre o Gemfile.lock

Parece óbvio, mas é o alicerce: um Gemfile.lock existente é sempre honrado como está pelo Bundler

Lockfile versionado é o que faz build de hoje e build de amanhã instalarem exatamente as mesmas versões

  1. Ative o frozen para bloquear divergência

A configuração frozen do Bundler proíbe mudanças no Gemfile: se o Gemfile mudou e o lockfile não foi atualizado, os comandos do Bundler são bloqueados

Ele assume true quando --deployment é usado

Na sua máquina de desenvolvimento, se precisar desligar:

bundle config set frozen false

O erro comum deste passo: desligar o frozen em dev, esquecer, e nunca mais perceber que o lockfile e o Gemfile andaram se separando

  1. Habilite o cooldown

Esse é o recurso mais direto ao ponto do problema

O Bundler 4.0.13 introduziu o cooldown, um filtro por tempo que recusa resolver para uma versão até ela estar pública há pelo menos N dias

É opt-in, e você declara no Gemfile:

source "https://rubygems.org", cooldown: 7

Dá pra sobrescrever por linha de comando quando for necessário:

bundle install --cooldown 0
BUNDLE_COOLDOWN=0 bundle update rails

O cooldown atua na resolução: bundle install sem lockfile, ou bundle update pra re-resolver

E ele complementa, não substitui, 2FA obrigatório e trusted publishing

O erro comum deste passo: configurar o cooldown e esperar que ele mude alguma coisa num bundle install com lockfile já resolvido

Não muda

Lockfile existente é honrado como está, o cooldown só entra quando há resolução acontecendo

Passo a passo: rotacionar a chave de API do RubyGems

Se você publica gem, essa parte é a que fecha a porta

A ordem abaixo é a recomendada pelo próprio advisory

  1. Rode gem signout em todos os hosts onde a chave legada foi usada
gem signout

O gem signout encerra todas as sessões atuais e remove o arquivo ~/.gem/credentials

Se você tiver a opção :credential_store: configurada no gemrc (ou a variável RUBYGEMS_CREDENTIAL_STORE), a chave fica guardada nesse credential store, e o gem signout remove dele todas as chaves do RubyGems, inclusive as salvas para outros hosts com gem signin --host

Olho nesse detalhe: "todos os hosts" quer dizer sua máquina, a máquina antiga, o container, o runner

Chave esquecida em ambiente esquecido é o clássico dos clássicos

  1. Garanta o cliente atualizado
gem update --system

O advisory pede rubygems 3.2.0 ou mais novo, justamente porque o caminho antigo de sign-in era o que gerava a chave legada

  1. Crie a nova chave no RubyGems.org

Você pode ir direto em https://rubygems.org/profile/api_keys

Ou pelo caminho na interface: clique no seu nome de usuário logado, depois em Settings e então em API Keys, e use New API Key

A senha da conta é pedida pra confirmar identidade (foi por isso que eu mandei separar ela lá nos pré-requisitos)

  1. Rode gem signin para gerar a nova chave
gem signin

O gem signin pede as credenciais do RubyGems.org, o nome da chave e os escopos a habilitar para ela

O erro comum deste passo: passar batido na tela de escopos e sair com uma chave que pode tudo

Pra publicar, push_rubygem já basta

Passo a passo: revisar owners e trusted publishers depois da rotação

Rotacionar a chave e parar aí é o equivalente a trocar a fechadura sem conferir quem já tem cópia

O advisory é explícito: adicionar um owner ou registrar um trusted publisher persiste mesmo depois de a chave ser revogada

  1. Revise a lista de donos da gem

O comando gem owner gerencia os donos

E aqui vale saber o tamanho do estrago possível: todo owner de uma gem tem as mesmas permissões, incluindo publicar novas versões, dar yank nas existentes e adicionar ou remover outros owners

Não existe "owner meia boca", todo mundo na lista tem a chave do reino

  1. Confira os trusted publishers da gem

Na página da gem, clique em Trusted publishers no lado direito

Se tiver algo registrado que você não reconhece, esse é o seu incidente

  1. Se for o caso, crie o seu trusted publisher com Create

O Trusted Publishing do RubyGems.org usa OpenID Connect para trocar tokens de identidade de curta duração entre o GitHub Actions e o RubyGems.org, dispensando API token para publicar

Sacou a sacada? Não existe chave pra vazar se não existe chave

Do lado do workflow, ele precisa da permissão id-token: write (recomendada no nível do job) e usa a action rubygems/release-gem@v1:

jobs:
  release:
    permissions:
      id-token: write
    steps:
      - uses: rubygems/release-gem@v1

O erro comum deste passo: configurar o trusted publisher e deixar a API key antiga viva no secret do repositório "por garantia"

Garantia de quê, exatamente? 😀

Escopos e MFA: como deixar a conta menos atraente para o próximo ataque

As chaves de API do RubyGems têm escopos, que concedem privilégios específicos

Uma chamada falha se a chave não tiver o escopo necessário, e publicar uma gem exige push_rubygem

É menor privilégio na prática: quanto mais estreita a chave, menor o impacto se ela vazar

Escopo Quando faz sentido
push_rubygem chave de release usada no CI, que só precisa publicar versão
yank_rubygem uso pontual, quando você realmente vai remover uma versão
add_owner reservado, mudança de ownership é evento raro e deliberado

A lógica é essa: se a chave do pipeline só sabe publicar, uma chave vazada não vira alguém se adicionando como dono da sua gem

E o guia oficial de segurança do RubyGems reforça o outro lado da conta: autenticação multifator, preferindo WebAuthn com chave de segurança ou passkey, por resistir a phishing

Código por SMS ou app é melhor que nada, mas phishing come esses no café da manhã

Conclusão: resultado da auditoria e o próximo passo

Se você seguiu o roteiro até aqui, o resultado é conferível, não é sensação de dever cumprido:

  • lista de dependências novas do lockfile revisada, com bundle-audit check rodado e os metadados das gems novas olhados um a um
  • versões travadas, com Gemfile.lock commitado, frozen ligado e cooldown declarado no Gemfile
  • chave antiga invalidada, com gem push, gem yank e gem owner retornando HTTP 401 se alguém tentar usar a legada
  • owners e trusted publishers conferidos, que é a parte que a rotação sozinha não resolve

O próximo passo natural é tirar isso do manual: colocar o bundle-audit no CI pra rodar em todo pull request e avaliar a migração do release para trusted publishing

Assim você elimina a chave que precisaria ser rotacionada na próxima vez que uma notícia dessas aparecer

E, sobre o episódio em si, a pergunta que fica aberta é a que o Simon Willison levanta na análise dele: quantos incidentes parecidos com esse ainda estão por aí esperando pra serem descobertos?

Ninguém sabe responder isso hoje, e é justamente por isso que auditar dependências Ruby vira hábito, não reação

Até o próximo post!

Perguntas frequentes

Como saber se minha chave de API do RubyGems foi revogada na rotação de julho de 2026?

Se você fazia sign-in no rubygems.org com cliente gem anterior à versão 3.2.0, ou usava chave legada por outro caminho, sua chave estava no grupo revogado. Na prática, o teste é rodar um gem push, gem yank ou gem owner: se voltar HTTP 401, a chave legada expirou e precisa ser trocada.

gem install e bundle install param de funcionar depois da revogação das chaves legadas?

Não. Como só as chaves legadas foram revogadas, gem install e bundle install continuam funcionando normalmente, porque são operações anônimas. O que quebra é justamente o próximo gem push, gem yank ou gem owner feito com a chave antiga.

Qual o passo a passo pra rotacionar a chave de API do RubyGems.org?

É o mesmo roteiro detalhado na seção de rotação deste post: rodar gem signout em todos os hosts onde a chave legada foi usada e garantir rubygems 3.2.0 ou mais novo com gem update –system. Depois, criar a nova chave em rubygems.org/profile/api_keys (ou pelo caminho Settings e API Keys, com New API Key) e rodar gem signin pra gerar a nova credencial, escolhendo nome e escopos.

O GemStuffer tem relação com o ataque de agentes da OpenAI ao RubyGems?

O GemStuffer é o nome que a Socket já tinha dado antes à campanha de publicação em massa de pacotes no RubyGems. Depois, Spencer Kitts, Thomas Larsen e Sydney Von Arx publicaram em rubyhack.ai um relatório atribuindo esse mesmo ataque a um enxame de agentes da OpenAI, mas o RubyGems.org afirma que não é possível confirmar essa atribuição com as evidências disponíveis a eles.

Trusted publishing elimina a necessidade de rotacionar chave de API no RubyGems?

Sim, é justamente essa a vantagem, como mostrei na seção de trusted publishers: o trusted publishing do RubyGems.org dispensa o uso de API token pra publicar. Sem chave no processo de release, não sobra credencial pra rotacionar nem pra vazar.

O bundler-audit precisa de internet pra checar as dependências do projeto?

A checagem em si do Gemfile.lock contra o ruby-advisory-db não precisa de conexão de rede. Só a atualização da base de advisories exige internet, seja rodando bundle-audit update separado, seja usando a opção –update (-u) junto do bundle-audit check.



Escrito por | Matheus Battisti

Matheus Battisti
Fundador da Hora de Codar

Programador apaixonado pelo mundo das tecnologias, sempre buscando em aprender e se aprofundar em linguagens, frameworks e o que mais for necessário para executar um bom trabalho. Agora tem uma nova missão que é de passar seu conhecimento adiante para formar novos programadores e especializar mais os que já são.

Subscribe
Notify of
guest

0 Comentários
Oldest
Newest Most Voted

Formações

Formação Vibe Coding

Formação Vibe Coding

Do Prompt ao Produto: Crie Software Real com IA

  • 474 aulas
  • 20 projetos
  • 39h 27min

Blog | Mais populares