In this article
Boas práticas de segurança no npm: como proteger seus pacotes após o ataque Shai Hulud de 2025
Ataques como Shai-Hulud, Nx, event-stream, colors, node-ipc e outros mostram que gerenciadores de pacotes são mecanismos de execução, não apenas ferramentas para baixar bibliotecas. Precisamos tratar o gerenciamento de dependências de terceiros com atenção e garantir a implementação de controles de segurança adequados.
A seguir, você encontra uma lista prática e selecionada de recomendações para reforçar a segurança do gerenciador de pacotes npm, voltada ao desenvolvimento local seguro e aos processos de mantenedores de software de código aberto. Deixe-a aberta ao lado do terminal.
Veja a seguir os principais destaques de práticas, ferramentas e áreas de segurança para orientar o uso responsável por desenvolvedores e mantenedores de código aberto:
Opções de CLI seguras por padrão para npm, pnpm, Bun, Yarn e Deno
Reforço da segurança da cadeia de suprimentos e instalações determinísticas
Higiene de lockfiles e dependências
Verificações de vulnerabilidades e integridade dos pacotes
Gerenciamento de segredos e isolamento do ambiente de desenvolvimento
Práticas para mantenedores: 2FA, proveniência, OIDC e redução de dependências
Sobre o Shai-Hulud e o malware da cadeia de suprimentos
A família de ataques Shai-Hulud marca um ponto de virada para desenvolvedores JavaScript: npm install agora é claramente um mecanismo de execução remota de código, não uma conveniência inofensiva. Em setembro de 2025, a campanha original Shai-Hulud usou versões adulteradas de pacotes como ngx-bootstrap, ng2-file-upload e @ctrl/tinycolor para distribuir uma carga maliciosa semelhante a um worm por meio de scripts de ciclo de vida do npm. Hooks maliciosos de postinstall baixaram um arquivo bundle.js ofuscado, que foi executado em máquinas de desenvolvedores e agentes de CI, coletando credenciais do npm, GitHub e serviços de nuvem e exfiltrando-as por meio de webhooks e workflows do GitHub. Centenas de pacotes acabaram envolvidos.
Em 24 de novembro de 2025, o SHA1-Hulud surgiu como uma segunda onda da mesma ideia, mostrando a rapidez com que os adversários estão evoluindo. Essa variante se propaga por pacotes npm trojanizados que ocultam cargas maliciosas em scripts preinstall. Depois que o pacote é instalado, o worm tenta transformar a vítima em um runner self-hosted do GitHub Actions controlado pelo invasor, injeta workflows maliciosos nos repositórios e os usa para executar comandos arbitrários e roubar segredos do npm e do GitHub. Ele procura ativamente credenciais da AWS, Azure e GCP e, em alguns casos, tenta até escapar de contêineres, elevar privilégios no host e executar ações destrutivas de “limpeza” no diretório pessoal do usuário. Até a publicação deste artigo, mais de 600 pacotes — incluindo pacotes populares de fornecedores como Zapier, PostHog e Postman — haviam sido identificados como parte da campanha, e esse número continua crescendo.
Juntos, Shai-Hulud e SHA1-Hulud definem um roteiro claro para o malware moderno da cadeia de suprimentos. Eles abusam dos hooks de ciclo de vida do npm como superfície de execução, transformam estações de trabalho de desenvolvedores e infraestrutura de CI/CD em pontos de propagação e têm como objetivo real credenciais, tokens e segredos de serviços de nuvem. Os detalhes de cada incidente ainda estão evoluindo, mas o padrão já é claro o bastante para extrairmos práticas duradouras que devem fazer parte dos reflexos de todo desenvolvedor JavaScript.
Este guia prático transforma essas lições em um conjunto concreto de práticas que você pode adotar hoje: configurar gerenciadores de pacotes seguros por padrão, reforçar a proteção contra ataques à cadeia de suprimentos, exigir resolução de dependências determinística e segura, incorporar verificações contínuas de vulnerabilidades e integridade dos pacotes e aplicar cada controle ao npm, pnpm, Bun e ao restante da sua cadeia de ferramentas. O objetivo não é reagir especificamente ao Shai-Hulud, mas tornar seu uso diário do npm resiliente à categoria de ataques que ele representa.
Índice rápido
Desative scripts post-install
Instale com período de espera
Reforce a segurança das instalações com
npqEvite a injeção de lockfiles
Use instalações determinísticas (
npmcietc.)Evite atualizações sem análise
Não armazene segredos em texto simples em
.envDesenvolva em contêineres
Ative a 2FA no npm
Publique com proveniência
Publique com OIDC (publicação confiável)
Reduza sua árvore de dependências
1. Desative scripts post-install
Seu objetivo é impedir a execução de código arbitrário durante o install.
Risco
Scripts post-install (e outros scripts de ciclo de vida) são um dos principais vetores de ataque à cadeia de suprimentos (Shai-Hulud, Nx, event-stream). Qualquer dependência, direta ou transitiva, pode executar código arbitrário durante a instalação.
Reforço básico da segurança do npm
Globalmente: desative todos os scripts de ciclo de vida (recomendado):
# Safe-by-default on your machine
npm config set ignore-scripts truePor instalação:
# One-off installs without running lifecycle scripts
npm install --ignore-scripts <package-name>pnpm
A partir da versão 10, o pnpm desativa scripts postinstall por padrão e oferece suporte a uma lista de permissões ou à reativação. Recomendamos consultar a documentação de segurança da cadeia de suprimentos do pnpm para ver outras opções de configuração.
Bun
O Bun desativa scripts postinstall por padrão e mantém uma lista de permissões interna. Você pode confiar explicitamente em determinadas dependências por meio de trustedDependencies em package.json:
{
"trustedDependencies": [
"some-package",
"another-package"
]
}Execute apenas os scripts realmente necessários
Use uma lista de permissões em vez de confiar cegamente no package.json:
1# Use LavaMoat's allow-scripts to define where scripts may run
2npm install --save-dev @lavamoat/allow-scripts
3npx allow-scriptsCom o pacote npm allow-scripts do LavaMoat, você pode habilitar seletivamente scripts em pontos específicos do grafo de dependências para pacotes npm confiáveis que realmente precisam dos scripts preinstall ou postinstall, como bcrypt, playwright e outros.
2. Instale com período de espera
Seu objetivo é evitar versões “recém-lançadas e maliciosas” e armadilhas de typosquatting.
Risco
Os invasores exploram o SemVer e a resolução de “latest” publicando novas versões que são identificadas e removidas rapidamente. Se você instalar imediatamente, ficará na linha de frente do impacto.
npm: instalações com base em data
Fixe a instalação apenas em versões publicadas antes de uma data:
# Install only if published before 2025-01-01
npm install express --before=2025-01-01Período dinâmico de espera de 7 dias (exemplo de date no estilo BSD):
npm install express --before="$(date -v -7d)"
Observação: esse processo é manual e pouco confiável para automação, mas funciona como uma medida explícita e útil de segurança.
2.1 minimumReleaseAge no pnpm
Em pnpm-workspace.yaml:
minimumReleaseAge: 20160 # minutes; here: 2 weeksO pnpm recusará versões mais recentes que esse período, dando tempo para o ecossistema identificar lançamentos maliciosos ou com problemas.
2.2 Período de espera do Snyk em PRs automáticas
As PRs automáticas do Snyk para atualizar dependências ignoram versões com menos de aproximadamente 21 dias, reduzindo:
Atualizações para versões com bugs que são removidas rapidamente
Atualizações para pacotes publicados por contas comprometidas
Esse é um padrão de “período de espera integrado à automação”.
3. Reforce a segurança das instalações com npq
Seu objetivo é evitar a instalação de um pacote até que ele passe por verificações básicas de segurança e integridade.
Problema
Você executa:
npm install some-packagee não sabe se:
O pacote é uma imitação com erro de digitação de um pacote popular
Ele foi publicado ontem e ainda não teve uso
Ele tem vulnerabilidades conhecidas
Ele contém scripts pre/post-install perigosos
Estratégia: use o npq antes das instalações
O npq é um auditor de segurança pré-instalação (usa “marshalls” para fazer verificações).
Instalação:
npm install -g npqUse no lugar do npm:
npq install expressDefina como padrão:
alias npm='npq-hero'
# Persist the alias
echo "alias npm='npq-hero'" >> ~/.zshrc # or ~/.bashrc
source ~/.zshrcAo usar o pacote npq, ele instala npq e pq-hero; este último funciona como um wrapper substituto para o npm.
O que o npq verifica (“marshalls”)
Vulnerabilidades usando o banco de CVEs do Snyk
Detecção de pacotes novos (menos de 22 dias)
Maturidade da versão (menos de 7 dias desde o lançamento)
Pacotes semelhantes usados em typosquatting
Verificação de assinatura do registro npm
Atestado de proveniência do build
Presença de scripts pre/post-install
Integridade do pacote: README, LICENSE, URL do repositório e downloads
Inclusão de binários (novas ferramentas de CLI)
Sinais de descontinuação
Validade do domínio do mantenedor/domínios expirados
Integração com pnpm e Bun
# One-off
NPQ_PKG_MGR=pnpm npq install fastify
NPQ_PKG_MGR=bun npq install fastify
# Make pnpm go through npq
alias pnpm="NPQ_PKG_MGR=pnpm npq-hero"4. Evite a injeção de lockfiles do npm
Seu objetivo é impedir que package-lock.json / yarn.lock redirecionem você silenciosamente para fontes maliciosas.
Risco
Um colaborador (ou um invasor por meio de uma PR) pode:
Adicionar um pacote malicioso ao lockfile.
Alterar a URL
resolvedpara apontar a um host sob seu controle (repositório Git, tarball, gist).Ajustar o hash de integridade para que pareça “válido”.
Assim, sua próxima execução de install baixa malware, mesmo que o package.json pareça inofensivo.
Mitigação: lockfile-lint
Instalação:
npm install --save-dev lockfile-lintValide o lockfile com hosts permitidos e HTTPS:
npx lockfile-lint \
--path package-lock.json \
--type npm \
--allowed-hosts npm yarn \
--validate-httpsPrincipais opções de validação
Validação de host – somente
npm,yarn,verdaccioetc.Exigência de HTTPS – rejeita esquemas de URL inseguros
Validação de esquema – permite apenas
https:,it+https: egit+ssh:Validação do nome do pacote – a URL resolvida corresponde ao nome do pacote
Validação de integridade – exige hashes seguros de integridade SHA-512
Integração com CI/CD
Adicione uma etapa de verificação do lockfile antes da instalação:
{
"scripts": {
"lint:lockfile": "lockfile-lint --path package-lock.json --type npm --allowed-hosts npm --validate-https",
"preinstall": "npm run lint:lockfile"
}
}pnpm e injeção de lockfiles
O pnpm e seu pnpm-lock.yaml são mais resistentes porque:
Não dependem de URLs arbitrárias de tarballs da mesma forma
Não instalam um pacote que esteja no lockfile, mas não esteja no
package.jsonO formato evita vários vetores de injeção encontrados no npm/yarn
Ainda assim, trate lockfiles como artefatos essenciais para a segurança.
5. Use instalações determinísticas (npm ci)
Seu objetivo é garantir que os builds e ambientes de produção usem exatamente as versões registradas no lockfile.
Risco
O npm install tenta “corrigir” divergências entre package.json e o lockfile, o que pode:
Baixar versões diferentes das registradas
Comprometer a consistência em CI/produção
Introduzir versões inesperadas, vulneráveis ou maliciosas
npm: use ci em vez de install
Ambiente local e CI:
# Deterministic install based on package-lock.json
npm ciSomente dependências de produção em CI/CD:
npm ci --only=productionGaranta que os lockfiles estejam no repositório e atualizados.
Comandos determinísticos em diferentes gerenciadores de pacotes
Yarn:
yarn install --immutable --immutable-cachepnpm:
pnpm install --frozen-lockfileBun:
bun install --frozen-lockfileDeno:
deno install --frozenLockfiles fazem parte do contrato da sua cadeia de suprimentos, não são artefatos de build que você pode ignorar.
6. Evite atualizações de pacotes npm sem análise
Seu objetivo é atualizar com revisão e dados de segurança, não “atualizar tudo para a versão mais recente”.
Risco
Evite atualizações de dependências de terceiros sem análise, como esta:
npm update
npx npm-check-updates -uAo executar esses comandos, seja em CI ou em ambientes de desenvolvimento local, você corre os seguintes riscos:
Baixar versões maliciosas publicadas por contas de mantenedores comprometidas
Incluir bugs que causam falhas e versões que acabam removidas
Disparar ataques de confusão de dependências ou sequestro de namespace
Práticas recomendadas
1. Atualizações interativas:
npx npm-check-updates --interactive2. Analise cada dependência antes de atualizar.
3. Bots com foco em segurança:
PRs automáticas de atualização do Snyk
PRs do GitHub Dependabot
4. Essas ferramentas criam PRs que você pode revisar, com informações contextuais (changelogs, CVEs), em vez de alterar seu lockfile silenciosamente.
7. Não armazene segredos em texto simples em arquivos .env
Seu objetivo é impedir que segredos sejam exfiltrados facilmente do seu ambiente de desenvolvimento.
Risco
Arquivos .env e variáveis de ambiente em texto simples:
São alvos fáceis para pacotes maliciosos ou malware de desenvolvimento.
Muitas vezes acabam em logs, dumps de falha, histórico do terminal etc.
São acessados por meio de
process.envou da leitura direta de arquivos em ataques à cadeia de suprimentos.
Veja um exemplo do que não fazer:
DATABASE_PASSWORD=my-secret-password
API_KEY=sk-1234567890abcdefPrática recomendada: referências a segredos e injeção sob demanda
Etapa 1 – Coloque referências (não valores) em .env:
DATABASE_PASSWORD=op://vault/database/password
API_KEY=infisical://project/env/api-keyEtapa 2 – Use a CLI do gerenciador de segredos em tempo de execução. Veja o exemplo da CLI do 1Password:
# Run app with secrets injected into process.env
op run -- npm start
# More explicit example with env-file
op run --env-file="./.env" -- node --env-file="./.env" server.jsOutras opções: CLI do Infisical, gerenciadores de segredos na nuvem etc. A ideia principal: a variável de ambiente contém uma referência, não o segredo.
8. Trabalhe em contêineres de desenvolvimento
Seu objetivo é isolar seu ambiente de desenvolvimento para que o malware do npm não comprometa sua máquina.
Risco
Executar npm install na sua máquina significa que:
Pacotes maliciosos podem ler arquivos de outros repositórios
Eles podem vasculhar chaves SSH, perfis de navegador, tokens de IA/agentes etc.
Eles compartilham o namespace do sistema operacional com todas as outras atividades
Padrão de contêiner de desenvolvimento
Use os Dev Containers do VS Code (ou ferramenta semelhante) para isolar o ambiente, como neste arquivo de configuração do Dev Container .devcontainer/devcontainer.json:
{
"name": "Node.js Dev Container",
"image": "mcr.microsoft.com/devcontainers/javascript-node:18",
"features": {
"ghcr.io/devcontainers/features/1password:1": {}
},
"postCreateCommand": "npm ci"
}Depois:
Abra a pasta no VS Code
“Reabrir no contêiner”
Todas as instalações e execuções acontecem dentro do contêiner.
Reforce a segurança do contêiner de desenvolvimento
Adicione opções de segurança do Docker e flags seguras do Node:
"runArgs": [
"--security-opt=no-new-privileges:true",
"--cap-drop=ALL",
"--cap-add=CHOWN",
"--cap-add=SETUID",
"--cap-add=SETGID"
],
"containerEnv": {
"NODE_OPTIONS": "--disable-proto=delete"
}Para ter ainda mais controle, use um Dockerfile personalizado com imagens base mínimas, um usuário sem privilégios de root e um ambiente de execução reforçado.
9. Ative a 2FA nas contas do npm
Seu objetivo é impedir que o comprometimento de uma conta resulte na publicação de versões maliciosas.
Risco
Incidentes como o do eslint-scope mostraram que, quando um invasor obtém credenciais, pode publicar versões comprometidas para milhões de usuários. A autenticação apenas por senha não é suficiente.
Comandos
2FA para login, publicação e alterações no perfil:
npm profile enable-2fa auth-and-writes2FA apenas para login e alterações no perfil (menos rigoroso):
npm profile enable-2fa auth-onlyAplique autenticação e restrições de escrita a todas as contas que possam publicar ou adicionar mantenedores.
Configure a publicação confiável com OIDC
Além de configurar sua conta npm com controles de senha adequados, como 2FA e Passkey, você também deve usar o novo método de publicação confiável com OIDC como a única forma de publicar novos pacotes npm, vinculando-os diretamente ao seu repositório do GitHub e a fluxos de trabalho específicos. Consulte a seção dedicada mais adiante.
10. Publique com atestações de proveniência
Seu objetivo é permitir que os consumidores verifiquem onde e como seu pacote foi produzido.
Problema
Sem informações de proveniência, os consumidores não conseguem saber facilmente:
Este arquivo tarball foi criado a partir do código-fonte A no GitHub ou em uma máquina mal-intencionada?
Um pipeline de CI comprometido injetou código?
Solução: npm publish --provenance
No GitHub Actions:
permissions:
id-token: write
steps:
- run: npm publish --provenanceRequisitos:
npm CLI 9.5.0+
GitHub Actions (ou GitLab CI/CD) com executores hospedados na nuvem e suporte a OIDC
Isso gera metadados de compilação verificáveis criptograficamente, alinhados a padrões emergentes de segurança da cadeia de suprimentos (por exemplo, OpenSSF).
11. Publique com OIDC (publicação confiável)
Seu objetivo é eliminar tokens npm de longa duração do CI/CD.
Risco
Tokens de longa duração:
Podem ser registrados ou incluídos em commits acidentalmente
Continuam válidos mesmo após vazamentos
Concedem acesso amplo e duradouro à sua organização e aos seus pacotes
Padrão de publicação confiável
Configure o pacote como um publicador confiável em npmjs.com (GitHub ou GitLab).
Use OIDC no seu fluxo de trabalho:
Exemplo com GitHub Actions:
permissions:
id-token: write
steps:
- run: npm publishNenhum NPM_TOKEN é armazenado em lugar algum. O npm verifica o token OIDC do seu CI e só permite publicações a partir dos fluxos de trabalho aprovados. As atestações de proveniência são geradas automaticamente.
12. Reduza a árvore de dependências do seu pacote
Seu objetivo é ter um grafo de dependências menor, o que, por sua vez, reduz a superfície de ataque e os riscos a que você fica exposto.
Risco
Cada dependência:
Traz suas próprias dependências transitivas
Depende de mantenedores e contas, que também podem ser comprometidos
Amplia sua exposição a vulnerabilidades e licenças
Estratégia
Prefira projetos sem dependências ou com poucas dependências. Use JavaScript moderno em vez de adicionar uma biblioteca utilitária para tarefas simples.
Exemplos:
// Instead of lodash uniq
const unique = [...new Set(array)];
// Instead of axios for simple HTTP GET
const response = await fetch(url);
// Instead of utility libs for trivial checks
const isEmpty = obj => Object.keys(obj).length === 0;Antes de adicionar uma dependência, pergunte a si mesmo (ou à sua equipe):
Essa funcionalidade é realmente complexa?
Ela justifica o custo de segurança e manutenção?
Já existe uma API padrão para isso?
O futuro da segurança para desenvolvedores e do malware na cadeia de suprimentos
Este guia apresenta algumas práticas recomendadas de segurança para npm, publicadas originalmente em 2019, e as aprimora e amplia com práticas modernas e aprendizados dos ataques à cadeia de suprimentos que presenciamos em 2025.
Primeiro, os desenvolvedores precisam tratar o gerenciador de pacotes como um mecanismo de execução não confiável e configurá-lo com opções seguras por padrão. Isso significa desativar scripts de ciclo de vida sempre que possível, recorrer a listas de permissões explícitas quando os scripts forem realmente necessários e usar mecanismos como períodos de espera e auditorias antes da instalação para que “instalar um novo pacote” seja uma decisão consciente, não um reflexo. Esses controles devem ir além do próprio npm e abranger pnpm, Bun e outros gerenciadores de pacotes modernos, garantindo configurações padrão consistentes em todas as ferramentas do espaço de trabalho.
Segundo, a resolução de dependências precisa ser determinística e confiável. As campanhas Shai-Hulud exploraram o fato de que uma única atualização de versão despercebida ou alteração no arquivo de lock poderia introduzir um tarball malicioso em milhares de projetos. Em resposta, as equipes devem estruturar os fluxos de trabalho em torno de arquivos de lock estritos e congelados, impor esse comportamento no CI e proteger esses arquivos com ferramentas que validem a origem dos pacotes. Instalações determinísticas não servem apenas para garantir a reprodutibilidade: elas permitem responder à pergunta “que código executamos e de onde ele veio?” quando ocorre um incidente.
Terceiro, vulnerabilidades e indicadores de integridade das dependências devem ser tratados como um fluxo contínuo de dados, não como um relatório ocasional. Incidentes como esses avançam mais rápido do que a publicação tradicional de CVEs, então suas defesas precisam combinar bancos de dados de vulnerabilidades, alertas de malware, atestações de proveniência, sinais como idade e popularidade dos pacotes e políticas automatizadas de atualização com períodos de espera integrados. Barreiras de segurança no momento da instalação, PRs automatizados que evitem versões muito recentes e ferramentas capazes de distinguir uma pequena correção de bug de uma versão não confiável e inédita de um pacote são componentes essenciais dessa estratégia.
Por fim, proteger o npm de forma eficaz não se resume a instalar dependências. Também depende de como você estrutura o ambiente de desenvolvimento local, gerencia segredos e publica e mantém seus próprios pacotes. Trabalhar em contêineres de desenvolvimento reforçados limita o impacto quando um pacote malicioso é detectado. Recomendo que você não use segredos em texto simples nas variáveis de ambiente; substituir arquivos .env em texto simples pela injeção de segredos sob demanda reduz o que um invasor pode roubar, mesmo que consiga executar código. Ativar 2FA, usar publicação confiável com OIDC e incluir atestações de proveniência nos seus próprios pacotes npm aumenta a dificuldade para quem tentar roubar sua identidade no ecossistema.
Mantenha-se protegido com a Snyk
A Snyk acompanha de perto a situação da Shai-Hulud e lança atualizações regularmente pela plataforma. Até ontem (24 de novembro), já havíamos identificado mais de 800 pacotes maliciosos.
Reunimos abaixo alguns recursos para ajudar você a ter mais controle sobre o incidente atual e a se preparar para a próxima vez.
Lista de pacotes comprometidos pela Shai-Hulud
A Snyk mantém uma página pública com a lista de pacotes maliciosos da Shai-Hulud associados à campanha de malware:

Relatórios Zero-Day da Snyk
Ao conectar seus repositórios à Snyk ou monitorá-los de outra forma, você cria um inventário que permite acompanhar facilmente vários aspectos das suas dependências, como auditar toda a organização de P&D para verificar se foi afetada pelo malware Shai-Hulud.
Para saber se você foi afetado por esse incidente, acesse Reports > Featured Zero-Day Report. Para saber mais sobre esse recurso, leia nossa Documentação do usuário.
As capturas de tela a seguir mostram como encontrá-lo na interface da Snyk (exibindo o ataque anterior à cadeia de suprimentos npm da Shai-Hulud, ocorrido em setembro de 2025). O novo relatório se chama SHA1-Hulud npm Supply Chain Attack - Nov 2025:

Monitore suas dependências com a Snyk
O uso de bibliotecas de código aberto exige monitoramento contínuo e atenção às atualizações de segurança, sejam vulnerabilidades CVE ou campanhas de malware, em toda a árvore de dependências — diretas e indiretas — da sua organização.
Com a Snyk, você pode conectar seus repositórios Git e assumir o controle proativo dos riscos de código aberto em projetos JavaScript com o Snyk Open Source como ferramenta de SCA. Você também pode — e deve — manter uma SBOM, recurso que a Snyk oferece pronto para uso.
Também recomendamos que você se prepare hoje para as vulnerabilidades Zero-Day de amanhã.
Prepare-se para vulnerabilidades de dia zero com a Snyk
Saiba como a Snyk pode ajudar seus desenvolvedores a corrigir vulnerabilidades de dia zero mais rapidamente, reduzindo a exposição e os riscos.