Dependency Confusion e Typosquatting: Defesa Pratica para Times Dev
Como politicas de registry, lockfiles e scoping bloqueiam pacotes maliciosos antes que cheguem ao build. Guia tecnico hands-on do time Basilisk.

Em 2021 um pesquisador faturou mais de 130 mil dolares de bug bounty publicando pacotes com nomes internos da Apple, Microsoft, PayPal e dezenas de outras empresas no npm e PyPI publicos. O ataque nao usou zero-day nenhum: apenas explorou a precedencia que gerenciadores de pacotes dao ao registry publico quando o nome existe nos dois lados. Cinco anos depois, equipes Basilisk ainda encontram esse vetor aberto em sete de cada dez pipelines auditados. Dependency confusion e typosquatting nao sao curiosidade academica, sao a porta de entrada barata pra comprometer um build inteiro e, por consequencia, todo cliente que recebe aquele artefato. Este post desmonta os dois vetores, percorre a cadeia de ataque passo a passo e te entrega uma defesa pra commitar na sua CI hoje mesmo.
O que dependency confusion significa tecnicamente
O mecanismo e simples e cruel. Voce tem um pacote interno chamado acme-payments-sdk hospedado num Nexus ou Artifactory privado. Um atacante descobre esse nome em um package.json vazado, num post de Stack Overflow ou num docker layer publico, e publica acme-payments-sdk@99.0.0 no npmjs.com. Na proxima vez que o pipeline roda npm install sem um lockfile estrito, o resolver escolhe a versao maior, porque a config padrao inclui o registry publico e o SemVer prefere a maior versao compativel. Pronto, codigo arbitrario rodando dentro do seu CI com credenciais AWS, tokens do GitHub e acesso ao registry interno. Vimos isso em campo em testes parecidos com os documentados em Pentest de APIs REST e GraphQL: Checklist Tecnico para Bug Bounty Legal, onde a superficie de build era mais explorada que a propria API. A raiz e que npm, pip e outros compartilham um namespace plano entre interno e publico e, sem config explicita, nao exigem prova de origem.
Typosquatting como vetor proprio
Typosquatting joga em outra liga. Em vez de assumir o nome real, o atacante registra colorss, requets, python-dateutill, lodahs ou djanga. O payload mora num hook postinstall ou direto no modulo importado e roda assim que alguem erra a digitacao ou uma IA sugere um pacote alucinado (o chamado efeito slopsquatting). Pesquisa da Phylum em 2024 catalogou mais de onze mil pacotes maliciosos no PyPI usando esse padrao em doze meses. Os payloads variam: roubo de variaveis de ambiente, mineracao, instalacao de RAT, exfiltracao via DNS do ~/.aws/credentials. A primeira defesa e cultural: code review obrigatorio em qualquer alteracao de dependencia, sem subir versao no escuro. Ferramentas como Socket, Snyk Advisor e deps.dev ja sinalizam pacotes recem-publicados, sem historico de mantenedor ou com padroes suspeitos de install script, te dando uma assinatura de risco antes do merge.
A cadeia de ataque passo a passo
Entenda o atacante e voce entende a defesa. Fase um e reconhecimento: o atacante colhe nomes internos em buscas no GitHub por @acme, em .npmrc vazados, em sourcemaps do bundle frontend ou em mensagens de erro em logs de CI publicos. Fase dois e publicacao: registra o mesmo pacote com uma versao absurdamente alta no registry publico e enfia um hook preinstall que bate numa URL de callback. Fase tres e detonacao: seu CI baixa o pacote publico em qualquer build e o hook roda com os privilegios do runner. Fase quatro e persistencia e exfiltracao: o codigo le variaveis de ambiente, tokens OIDC e deploy keys e manda pra fora, muitas vezes por DNS ou um POST HTTPS de aparencia inocente. E exatamente aqui que o comprometimento de supply chain se funde com o post-exploitation classico, por isso vale a mesma cabeca de resposta a incidente.
Lockfiles e hash pinning
Lockfiles resolvem oitenta por cento do problema se forem usados serio. package-lock.json, yarn.lock, pnpm-lock.yaml, poetry.lock, Cargo.lock e go.sum amarram nao so a versao mas o hash de integridade. Configure npm ci, pnpm install --frozen-lockfile, pip install --require-hashes e cargo --locked nos pipelines. Nada de npm install solto em producao, porque ele pode mutar o lockfile. Combinado com um .npmrc que define registry=https://nexus.interno/repository/npm-group/ e always-auth=true, voce reduz drasticamente a chance do resolver ir buscar no registry publico por engano. Ponto-chave: o lockfile so protege se o runner de CI respeitar e nao regenerar. O padrao mental e parecido com o que discutimos em Supply Chain Security: Assinatura com Sigstore e SBOM Real em CI/CD quando falamos de SBOM e atestacoes.
Scoping e registries privados por ecossistema
Scoping e a arma definitiva contra dependency confusion no ecossistema JavaScript. Migre todos os pacotes internos para @acme/payments-sdk, @acme/auth, @acme/billing. No .npmrc, defina @acme:registry=https://nexus.interno/. Agora o npm so resolve esse escopo no registry interno, mesmo que alguem publique @acme/payments-sdk no npmjs (a Microsoft reserva escopos comuns desde 2021). Em Python, use indexes separados: pip --index-url para internos e --extra-index-url so quando realmente preciso, porque os dois indexes sao misturados e a versao maior vence. Melhor, espelhe tudo via devpi ou Artifactory e defina somente index-url. Para Go, GOPRIVATE=git.acme.com mais disciplina de GONOSUMCHECK bloqueia consultas a proxy.golang.org. Esse hardening dialoga direto com o que cobrimos em Hardening de Linux Servidor: CIS Benchmark Aplicado sem Quebrar Producao sobre principio do menor privilegio.
Isolamento de CI e credenciais efemeras
No lado do CI, isole sem doer. Cada job de build deve rodar com tokens efemeros, sem acesso a producao, com saida de rede filtrada por um egress proxy permitindo apenas registry interno, github.com e endpoints conhecidos. OIDC com short-lived credentials no GitHub Actions ou GitLab CI elimina os secrets longos que um install hook poderia roubar. Desative install scripts onde der com npm ci --ignore-scripts e permita so para uma allowlist curada. Rode o build num container non-root, filesystem read-only, sem acesso ao socket do docker. Assim ate um install hook bem-sucedido vira faca cega: nao acha secrets longos, nao consegue telefonar pra casa e nao sobrevive ao fim do job. Essa segmentacao e mais barata que qualquer incidente e ainda protege o caso em que uma dependencia legitima seja sequestrada la em cima depois.
Verificacao antes de instalar
Adicione um step de verificacao pre-install: scripts/check-deps.sh roda npm audit signatures, valida que nenhuma dependencia foi publicada nos ultimos sete dias sem aprovacao manual (um cooldown contra sequestros frescos), e checa pacotes contra uma allowlist. Some osv-scanner contra a base OSV e Socket no CI, que mostra diffs de comportamento entre versoes: novas chamadas de rede, novo acesso a filesystem, install scripts recem-adicionados. Um pacote que de repente importa child_process ou contata um IP fica bloqueado e vai pra revisao manual. O time da Datadog publicou um relatorio em 2025 mostrando que sessenta e dois por cento dos comprometimentos de supply chain teriam sido bloqueados com essas tres regras combinadas. Nenhuma ferramenta sozinha e bala de prata, mas encadear cooldown, checagem de assinatura e diff de comportamento fecha os caminhos mais comuns.
Monitoracao continua e protecao de nomes
Monitoracao continua fecha o ciclo. Configure alertas no Sigstore Rekor para qualquer publicacao com seu nome de organizacao, registre defensivamente os dominios e nomes de pacote tipo-squatting do seu produto (acme-cloud, acme-coud, acmecloud), e mantenha um inventario de SBOMs assinados via cosign para cada release. Quando um pacote suspeito aparecer, voce ja tem evidencia para o takedown e dados para o incident response, no estilo do que mostramos em DFIR no Linux: Triagem ao Vivo com UAC e Velociraptor. Reserve proativamente os nomes dos seus pacotes internos como placeholders vazios no registry publico pra ninguem ocupar. Treine o time pra reportar qualquer warning de install script novo em vez de ignorar por reflexo, porque esse warning costuma ser o unico sinal antes do comprometimento.
Erros comuns na pratica
Cinco pitfalls aparecem em quase toda auditoria. Primeiro: --extra-index-url como padrao em Python, que mistura interno e PyPI e escancara o vetor. Segundo: lockfiles que sao regenerados em vez de respeitados no CI, porque alguem deixou npm install no lugar de npm ci. Terceiro: pacotes internos sem escopo, de modo que qualquer namespace publico os ofusca. Quarto: install scripts liberados globalmente mesmo que noventa por cento dos builds nao precisem. Quinto: tokens npm ou PyPI de vida longa no CI que um hook exfiltra e que seguem validos por meses. Cada um sozinho ja basta pra um comprometimento; juntos sao um portao aberto. A boa noticia e que cada um se corrige em menos de um dia e nenhum exige mudar o codigo do produto.
Checklist pratico
Toque essa lista hoje. Um: migre todos os pacotes npm internos para um @scope amarrado ao registry interno. Dois: force npm ci, --frozen-lockfile, --require-hashes ou --locked em todo pipeline. Tres: em Python use so index-url apontando pro mirror interno, sem --extra-index-url. Quatro: defina GOPRIVATE pra todos os modulos Go internos. Cinco: desative install scripts por padrao, mantenha allowlist. Seis: OIDC em vez de tokens de registry de vida longa. Sete: egress proxy no CI com allowlist. Oito: osv-scanner, npm audit signatures e Socket como gate de merge. Nove: reserve defensivamente nomes internos no registry publico. Dez: alertas do Rekor sobre o nome da organizacao. Marque esses dez e voce trancou a porta barata.
FAQ
Um lockfile sozinho basta contra dependency confusion? Nao. O lockfile protege dependencias existentes, mas assim que voce adiciona uma dependencia interna nova ou regenera o lockfile, a precedencia do registry morde de novo. Amarrar o escopo ao registry interno e um egress proxy sao a solucao estrutural; o lockfile e a segunda camada.
Typosquatting e so problema de npm e PyPI? Nao. RubyGems, Maven Central, NuGet, Crates e ate imagens do Docker Hub sao afetados. A abordagem defensiva e a mesma em todo lugar: mirror interno curado, diff de comportamento entre versoes, cooldown para artefatos recem-publicados e revisao humana em cada mudanca de dependencia.
Conclusao
Dependency confusion e typosquatting nao sao ataques exoticos, sao a forma mais barata de passar do lado de uma organizacao sem tocar numa unica linha do codigo real dela. A defesa e conhecida, barata e aplicavel hoje: amarre de escopo, hash pinning, CI isolado com credenciais efemeras, verificacao antes de instalar e monitoracao continua. Takeaway pratico: hoje mesmo, audite seus .npmrc, .pip.conf e go env. Se voce nao consegue responder em trinta segundos de qual registry cada dependencia vem, voce ja esta vulneravel. Os atacantes nao precisam de zero-day; so esperam alguem deixar a ordem do registry sem configurar.


