O que aconteceu em maio de 2026
Dois relatos da Microsoft no mesmo mês deixaram o problema difícil de ignorar. No dia 28, a pesquisa descreveu pacotes npm typosquatados sendo usados para roubar segredos de nuvem e de CI/CD. No dia seguinte, saiu o relato de 33 pacotes npm maliciosos abusando de dependency confusion para perfilar ambientes de desenvolvedor.
São dois vetores distintos que atacam o mesmo ponto fraco: a forma como o seu build resolve dependências. Se você entende como cada um funciona, a defesa fica quase óbvia. O difícil é que quase ninguém revisa a resolução de pacotes antes de um incidente.
Dependency confusion, sem mistério
A ideia é simples e por isso é perigosa. Sua empresa tem um pacote interno, digamos acme-auth, publicado num registry privado. O build sabe baixar acme-auth, mas nem sempre sabe de onde. Se esse nome não estiver fixado a um registry privado com prioridade, o resolver pode ir ao registry público e encontrar um homônimo publicado por um atacante.
O pacote público muitas vezes ganha por ter uma versão maior. O build baixa o impostor por engano, executa o postinstall dele e pronto: código do atacante roda dentro do seu ambiente. Nos 33 pacotes reportados, o objetivo era perfilar o ambiente do desenvolvedor, ou seja, coletar dados sobre a máquina e o pipeline para preparar o próximo passo.
Repare no detalhe: não houve invasão de servidor, não houve senha vazada. O atacante só publicou um nome que o seu build já procurava.
Typosquat, o vizinho de nome parecido
Typosquat é o clássico do erro de digitação. O atacante registra expres, lodahs, react-domm, nomes a um caractere de distância dos pacotes que todo mundo usa. Um dev cansado erra o npm install, um script de CI tem um typo antigo, e o pacote malicioso entra na árvore.
No caso de maio, esses pacotes typosquatados foram desenhados para roubar segredos de nuvem e de CI/CD. Faz sentido: o CI é onde os segredos vivem em texto utilizável no momento do build.
Por que o CI é alvo de ouro
Pense no que um runner de CI tem em mãos quando roda um install:
- Token de nuvem para deploy (AWS, GCP, Azure).
- Credencial de registry para publicar imagens e pacotes.
- Chave de assinatura, variáveis de ambiente,
.npmrccom token. - Acesso de rede a serviços internos.
E, na maioria dos pipelines, o postinstall de qualquer dependência roda com esse mesmo contexto, sem sandbox. Um pacote malicioso na árvore lê process.env, encontra o token de deploy e o exfiltra numa requisição de rede antes mesmo dos testes rodarem. O ataque não precisa passar pelos seus testes, ele acontece no install.
As defesas, priorizadas
Ordenei por retorno sobre esforço. As primeiras cortam a maior parte do risco com pouca mudança.
1. Trave o registry por escopo no `.npmrc`
Essa é a defesa mais barata contra dependency confusion. Coloque seus pacotes internos sob um escopo (@acme/) e amarre esse escopo ao registry privado:
@acme:registry=https://registry.interno.acme.com
//registry.interno.acme.com/:_authToken=${NPM_TOKEN}
Agora @acme/auth só pode vir do registry privado. Não existe homônimo público de um escopo que você controla, então o vetor de confusion simplesmente fecha para esses pacotes.
2. Use scoping e namespace em todo pacote interno
Pacote interno sem escopo é um nome global disputável. Migre acme-auth para @acme/auth. O escopo é registrado e controlado por você, o que remove a possibilidade de alguém publicar o mesmo nome no público. Vale o esforço de renomear.
3. Registry privado com prioridade, não como fallback
Configure o proxy ou registry privado como fonte primária, não como espelho que só é consultado quando o público falha. A ordem de resolução importa. Se o público for consultado primeiro, a confusion volta pela porta dos fundos.
4. Lockfile com integrity, sempre
Commite o package-lock.json (ou o lockfile do seu gerenciador) e instale com npm ci, não npm install, no CI. O npm ci respeita o lockfile e falha se algo divergir. O campo integrity (hash SHA-512 por artefato) garante que o byte que chega é o byte que você fixou. Uma versão trocada por baixo dos panos não passa.
5. Verificação de nome contra typo
Antes de adicionar dependência, confira o nome com calma. Ferramentas ajudam, mas a regra humana é simples: desconfie de pacote novo, com poucos downloads, publicado há pouco tempo, com nome a um caractere de um pacote popular. Um scanner de dependência (a RET tem o AuditMySaaS) pega esse tipo de sinal cedo, antes de o pacote entrar na árvore. Não substitui a revisão, adianta ela.
6. Segredo via OIDC e token de curta duração
Esse é o ponto que transforma um comprometimento em não evento. Troque credencial de longa duração fixada no CI por federação OIDC: o runner troca sua identidade por um token temporário, com escopo restrito, que expira em minutos. Se um pacote malicioso rodar no install e vazar o token, o token já morreu ou serve para quase nada. GitHub Actions, GitLab e os provedores de nuvem suportam isso hoje.
7. Least privilege no runner
O runner de CI não precisa de acesso de admin. Dê a cada job só a permissão daquele job: quem builda não precisa de deploy, quem testa não precisa de credencial de produção. Reduza escopo de token, isole o job de publish do job de teste, e negue rede de saída onde der. Menos poder no runner significa menos coisa para o atacante roubar.
8. Contenha o `postinstall`
Script de ciclo de vida é o gatilho preferido desses ataques. Onde a sua stack permitir, rode a instalação com --ignore-scripts e execute só os scripts que você auditou. No mínimo, saiba quais dependências rodam postinstall e por quê. Um pacote de utilidade que insiste em rodar script no install merece uma segunda olhada.
Uma nota honesta sobre scanners
Nenhuma ferramenta sozinha resolve isso. As defesas de configuração (escopo travado, registry com prioridade, OIDC) são o que de fato fecha o vetor. Um scanner de dependência não impede o ataque, ele te avisa cedo, quando um nome suspeito, uma versão nova estranha ou um typo entra no seu grafo. É rede de segurança, não muralha. Trate os dois como camadas: configuração que fecha a porta e scanner que vigia a fechadura.
Por onde começar hoje
Se você só tem uma hora, faça o item 1 e o item 6: trave o escopo dos seus pacotes internos no .npmrc e migre o segredo de deploy do CI para OIDC de curta duração. Esses dois cortam os dois vetores de maio de 2026 na raiz. O resto é endurecimento incremental, e cada item que você marca reduz a superfície de um jeito mensurável.
O ataque não invade o seu servidor, ele publica um nome que o seu build já estava procurando. Feche a resolução de pacotes e o segredo do CI antes que alguém teste isso por você.




