Skip to content
← Voltar para o Skalablog

Artigo publicado

5 flags do node --permission contra pacotes npm

A flag node --permission do Node.js bloqueia por padrão o acesso a arquivos, rede e processos filhos de qualquer aplicação. Um pacote npm minificado consegue roubar variáveis de ambiente em menos de 10 milissegundos, e o modelo de permissões do runtime é a barreira mais direta contra esse tipo de ataque de supply chain.

O que é um ataque de supply chain em npm?

Um ataque de supply chain em npm é a invasão do seu projeto pela cadeia de dependências, não pelo seu código. O atacante compromete um fornecedor pequeno, que dá acesso ao médio, e o médio ao fornecedor grande, até alcançar o servidor final. Em JavaScript, isso costuma entrar por um pull request que altera uma dependência menor no package-lock..

O caso mais citado é o do event-stream, pacote popular que recebeu uma dependência nova chamada flatmap-stream em 2018. O código injetado tentava roubar carteiras de bitcoin do aplicativo Copay, e a análise da Snyk detalhou como o payload ficou escondido por meses. A técnica é a mesma usada em demonstrações recentes: confiar no maintainer e nunca verificar o que chegou junto.

O princípio de defesa é simples de enunciar: confie, mas verifique. Não é porque o pull request veio de alguém conhecido que você deve aprovar sem revisar. A máquina dessa pessoa pode estar comprometida, e você abriria a porta para um invasor sem perceber.

Como um pacote minificado executa código malicioso em 8 ms

A demonstração de Erick Wendel usa um pacote fictício com uma única função, somar dois valores, no estilo do is-odd, que existe só para testar paridade e soma centenas de milhares de downloads por semana. O código-fonte público é inofensivo. O pacote publicado no npm, ofuscado, não é.

A mecânica do payload

Ao rodar, a função entrega o resultado correto e, em paralelo, escreve um script na pasta temp do sistema operacional. Ela usa child_process.spawn com a opção detached, o que marca o processo filho como independente do processo que o chamou, e unref, para o pai não esperar por ele. O script filho captura process.env inteiro, salva em arquivo, busca conteúdo em um site externo e ainda pode abrir um servidor web em modo zumbi na máquina.

O script se deleta da pasta temp logo após executar, para não deixar rastros. Todos os erros são engolidos em silêncio: se a escrita falha, se o site está fora do ar, nada aparece no console do desenvolvedor. Por isso os 8 milissegundos medidos parecem apenas o tempo normal de um cálculo trivial.

Por que a ofuscação engana a revisão

A versão publicada passa pelo javascript-obfuscator, que mistura hexadecimais, strings codificadas e estruturas ilegíveis. Ela parece apenas um build minificado comum. Um humano que abrir node_modules não reconhece o payload, e o autor ainda pode reescrever o histórico com git antes de publicar, então comparar com o repositório não garante nada.

O que a flag node --permission faz no Node.js

O modelo de permissões do Node.js restringe o que o processo pode acessar: sistema de arquivos, rede, processos filhos, workers e complementos nativos. Sem liberação explícita, qualquer acesso não autorizado é bloqueado na execução. A ideia foi desenvolvida por Rafael Gonzaga, brasileiro que integra a diretoria técnica do projeto Node.js.

Introduzido como experimental no Node.js 20 em 2023, o modelo atingiu status estável no Node.js 24, lançado em 2025. Na prática, você inicia a aplicação com node --permission e o runtime passa a negar tudo por padrão, liberando apenas o que você declarar. Para uma API que lê os próprios arquivos e grava no banco, o processo perde qualquer capacidade que fuja desse papel.

Vale um cuidado: processos filhos criados dentro de um processo com permissões não herdam as restrições automaticamente. Se o código malicioso usa spawn para lançar outro node sem flags, ele sai do escopo controlado. Você precisa restringir também o que o filho pode fazer, como a demonstração mostra ao passar as permissões para o script gravado na pasta temp.

Configurando allow-fs-read, allow-fs-write e allow-child-process

As flags do modelo de permissões são declarativas: você diz exatamente onde o processo pode ler, escrever e a que pode chamar. A tabela abaixo resume as principais usadas na demonstração.

FlagO que liberaExemplo
--permissionAtiva o modelo e bloqueia tudo por padrãonode --permission
--allow-fs-readLeitura de arquivos ou pastas específicas--allow-fs-read=./src
--allow-fs-writeEscrita apenas nos caminhos declarados--allow-fs-write=/tmp
--allow-child-processPermite spawn/fork de processos filhos--allow-child-process
--allow-net (opcional)Acesso a portas e hosts específicosconfigurado por aplicação

Na prática da demo, o script malicioso tenta gravar um arquivo com nome suspeito na temp. Com --allow-fs-read=./src, a leitura do seu index passa; sem --allow-fs-write liberado para aquele caminho, a tentativa de escrita falha com erro explícito apontando o arquivo estranho. O próprio erro do runtime revela o ataque antes de ele completar qualquer etapa.

Para o child process, a recomendação é dobrada: só libere --allow-child-process se a aplicação realmente spawnar processos, e rejeite qualquer pacote cuja funcionalidade não justifique essa necessidade. Uma biblioteca de soma não tem motivo nenhum para criar processos filhos.

Revisando o package-lock. com lockfile-lint

O package-lock. mapeia toda a árvore de dependências do projeto, e é justamente por ser gigante que a maioria dos times o ignora na revisão de código. É nesse arquivo que um atacante esconde a mudança de perigo: a dependência menor de um pacote conhecido passa a resolver para uma URL diferente, apontando para um repositório externo em vez do registro oficial do npm.

Cenário descrito pela Snyk: um pacote que você não conhece sobe de versão, muda só o último número e ninguém verifica. No deploy seguinte, a aplicação roda um ransomware que sequestra os dados e exige resgate em bitcoin. A revisão humana falha porque o diff do lockfile tem centenas de linhas e ninguém sabe ler aquilo.

O lockfile-lint, do pesquisador Liran Tal, valida o lockfile automaticamente: ele verifica se todas as URLs de resolução apontam para hosts confiáveis, como o registro do npm, e falha no CI se algum pacote resolver de origem inesperada. Incluí-lo no pipeline elimina a necessidade de revisar manualmente cada linha do arquivo em cada atualização.

Checklist de defesa para aplicações Node.js

A síntese do vídeo cabe em práticas concretas. Se você adota apenas uma, comece pela primeira:

  1. Rode sempre com node --permission e libere só o que a aplicação usa de verdade.
  2. Adicione o lockfile-lint ao CI para validar origens de dependências no package-lock..
  3. Revise todo pull request que altera dependências, mesmo de pessoas confiáveis.
  4. Ferramentas como a Snyk ajudam a detectar vulnerabilidades conhecidas nas dependências do projeto.
  5. Prefira rodar em containers ou ambientes isolados; em uma máquina virtual sem isolamento, o comprometimento pode escalar para outras aplicações.
  6. Desconfie de scripts minificados e assets vindos de CDN sem verificação de integridade.
  7. Exponha child_process apenas quando indispensável e restrinja as permissões do processo filho.

Isolamento e menor privilégio andam juntos. A flag restringe o processo; o container limita o dano se o processo for burlado. Uma camada não substitui a outra, e um time que combina as duas reduz drasticamente a superfície de ataque descrita aqui.

FAQ sobre ataques npm e node --permission

  • O node --permission funciona em qualquer versão do Node.js? Não. A flag foi introduzida como experimental no Node.js 20, em 2023, e passou a ser considerada estável no Node.js 24, de 2025. Em versões anteriores ela não existe ou não cobre todos os vetores de acesso.
  • Isso é uma vulnerabilidade do Node.js? Não. O runtime executa, por design, qualquer código que o processo possa alcançar, inclusive código de dependências. O problema é operacional: rodar código de terceiros sem restringir o que ele pode fazer.
  • Preciso revisar o package-lock. linha por linha? Com o lockfile-lint configurado, não. A ferramenta valida automaticamente as origens de resolução das dependências e falha quando alguma aponta para um host não confiável, como um repositório externo.
  • Por que o ataque passa despercebido se o código fica em node_modules? Porque o payload é ofuscado até parecer um build minificado comum, e o script apaga os próprios rastros da pasta temp. Sem erros no console e sem arquivo remanescente, não há sinal para o desenvolvedor.
  • Scripts minificados de CDN são sempre perigosos? O risco maior está em servidores, não no navegador. Em um servidor Node.js, um script trocado pode alcançar variáveis de ambiente, bancos e serviços internos; no navegador, o dano típico é capturar o que o usuário digita na página.

Da videoaula ao artigo: transforme esse conteúdo

Permissões, lockfiles e revisão de dependências são temas que vivem em vídeos longos, com demonstrações no terminal que nem sempre sobrevivem à memória de quem assistiu uma vez. Conteúdo técnico assim merece uma versão escrita, consultável, que a equipe possa buscar depois quando o alerta de segurança aparecer de verdade.

Se você grava aulas e demonstrações, seja sobre segurança em Node.js, TypeScript ou qualquer outro tema, dá para transformar esse material em artigo sem regravar nada. O Skala Blog faz esse caminho: você cola o link do vídeo do YouTube, a ferramenta transcreve e gera um artigo estruturado, pronto para revisar e publicar. Cursos como o Crazystack Typescript e o Bootcamp do Dev Doido, do canal do Gustavo Dev Doido, seguem a mesma lógica de aprendizado: quanto mais formatos o mesmo conhecimento ocupa, mais gente alcança.

Source video

YouTube thumbnail: Como um Pacote NPM Sequestra Seu Servidor em 10 ms

Source video

Como um Pacote NPM Sequestra Seu Servidor em 10 ms

Create from this video

This article was created automatically from a new video.

Automate my channel

Turn your YouTube videos into articles

Built for creators with a backlog and no editor on staff. This page started as a video — Skalablog drafts from the transcript, you review every word, then publish on a first-party /p/{slug} page. Nothing goes live until you publish it.

Turn your videos into articles

Published with Skalablog