Como evitar pacotes maliciosos
27 de março de 2016
0 minutos de leituraNa semana passada, o CERT alertou os usuários sobre o risco de publicar ou consumir um pacote malicioso do npm. Embora não seja exclusivo do npm, esse risco significativo é mais provável nesse ecossistema. Vale ressaltar que, embora seja definitivamente um vetor de ataque, na minha opinião não é uma vulnerabilidade, mas sim uma configuração padrão insegura.
Esta publicação explica o risco e como você pode se proteger. Vamos analisar as concessões que deram origem ao problema, examinar um cenário de exploração e mostrar como reduzir o risco nos seus próprios ambientes. Se quiser ir direto às ações recomendadas, pule para as seções sobre mitigação.
Talvez seja útil consultar 10 boas práticas de segurança para npm, onde você encontrará tanto um PDF para download com um guia rápido quanto um artigo detalhado sobre como adotar diretrizes de segurança e práticas seguras para o npm.
O ataque
Pacotes maliciosos em 5 passos simples
A facilidade de publicar um novo pacote está no cerne do problema. Assim como git, pip e outros, o npm permite que os usuários armazenem suas credenciais em um arquivo oculto (.npmrc) e publiquem sem solicitações interativas.
Infelizmente, este é um caso em que é possível facilitar demais. O processo simplificado de publicação aumenta a chance de publicar por engano, mas, ainda mais importante, abre caminho para que invasores publiquem código malicioso intencionalmente. Para publicar uma versão maliciosa de um pacote, o invasor só precisa:
Executar código na máquina do desenvolvedor.
Descobrir qual usuário do npm está conectado (
npm whoami --silent)Baixar um dos pacotes do usuário para uma pasta temporária
Editar package.json: adicionar um hook malicioso de
postinstalle incrementar a versão em 0.0.1Publicar (
npm publish)
Dessas etapas, a única desafiadora é a primeira. Infelizmente, a maioria dos desenvolvedores costuma executar seus ambientes com pouca restrição…
Você já instalou algo usando curl X | bash? Essa é uma forma de um invasor conseguir acesso — nem é preciso usar sudo bash. Ou talvez você às vezes instale um pacote npm localmente só para testá-lo? Qualquer uma das dependências dele pode ter um hook de postinstall que execute as ações acima.
O fato é que nós, desenvolvedores, experimentamos e, naturalmente, não usamos ambientes totalmente limpos. Esses ambientes não deveriam ter permissão para publicar.
Propagação rápida, detecção lenta
Depois disso, o invasor pode simplesmente relaxar enquanto o código se propaga pelo ecossistema Node. A maioria dos consumidores desse pacote vai carregá-lo usando um intervalo semver que inclui implicitamente versões de correção (0.0.X) e, assim, receberá essa nova versão sem perceber. Também é possível fazer o código malicioso se propagar para pacotes pertencentes às vítimas, criando um worm que se espalha ainda mais rápido.
Para piorar, se um pacote malicioso desses for publicado, não há uma maneira fácil de perceber. O npm não avisa o usuário quando uma nova versão é publicada, e não há uma forma simples de ver a diferença entre pacotes (ao contrário do git). Sua melhor chance de identificar um problema desses é perceber o novo número da versão. Se você mantém ativamente um projeto desenvolvido por uma única pessoa, provavelmente vai notar as mudanças de versão. Mas, em projetos inativos ou desenvolvidos por uma equipe inteira, elas podem passar facilmente despercebidas.
Técnicas de mitigação
Como evitar publicações maliciosas (pacotes públicos)
Se você é colaborador de um pacote npm, é sua responsabilidade se proteger. Não deixe que invasores publiquem uma versão maliciosa em seu nome. É simples: mantenha a sessão encerrada.
Publique somente pela sua CI quando uma compilação na branch master for concluída com sucesso, usando semantic-release ou scripts personalizados. Veja as instruções passo a passo:
Invalide todos os tokens atuais. Você pode fazer isso na página de tokens e deve pedir que todos os colaboradores façam o mesmo.
Configure um novo token na sua CI. Remy escreveu instruções detalhadas para o Travis, mas a maioria dos sistemas de CI é compatível com variáveis ocultas. Observe que, depois de configurar o token, executar
npm logoutvai invalidá-lo. Em vez disso, exclua o arquivo~/.npmrcpara encerrar a sessão.Para versões de desenvolvimento, use git. Se os usuários precisarem acessar uma versão temporária do pacote (por exemplo, durante a investigação de um problema), faça commit dela em uma branch e oriente-os a instalar usando um commit do git.
Como evitar publicações maliciosas (pacotes privados)
Se você usa um pacote privado, não pode manter a sessão encerrada, pois precisa estar conectado para acessar seus pacotes privados. Felizmente, o npm lançou recentemente as organizações e oferece suporte a usuários com acesso somente para leitura, permitindo conceder acesso sem correr o risco de publicação.
Veja novamente as instruções passo a passo:
Crie uma organização no npm. Se você tiver apenas um usuário, isso aumentará o custo mínimo em US$ 7 por mês, mas vale muito a pena.
Crie uma equipe com acesso somente para leitura aos seus pacotes ou ao seu escopo.
Adicione usuários a essa equipe como membros. Você também pode reutilizar um único usuário com acesso somente para leitura.
Peça à sua equipe (e a você) que faça login com esses usuários com acesso somente para leitura.
Essas etapas não eliminam a necessidade de ter cuidado ao instalar softwares aleatórios no computador, mas pelo menos reduzem a probabilidade de você se tornar um meio de distribuição de código malicioso.
Como se proteger de pacotes maliciosos
Como consumidor de pacotes do npm, você não consegue evitar totalmente esse risco (vale lembrar que isso também se aplica a outros gerenciadores de pacotes). A única forma de mitigá-lo é controlar quais pacotes você instala.
Se você desenvolve localmente, considere usar npm shrinkwrap para fixar as versões dos pacotes que utiliza. O shrinkwrap congela as versões em uso, sem adotar novas versões a menos que isso seja solicitado explicitamente. Assim, você pode analisar manualmente cada atualização. Isso dá trabalho, mas aumenta sua segurança.
Se você faz parte de uma organização, considere usar npm On-Site e incluir na lista de permissões os pacotes que você autoriza. Mais uma vez, isso dá um pouco de trabalho, mas reduz o risco de entrada de pacotes maliciosos. Para garantir a segurança, configure Read through cache como Off.
As duas soluções vão atrasar a adoção de novas versões e, por isso, podem deixar você exposto quando vulnerabilidades forem divulgadas em versões antigas desses pacotes. Se optar por elas, recomendo acompanhar de perto as vulnerabilidades conhecidas nas suas dependências do npm, por exemplo, usando o Snyk.
O npm não pode simplesmente corrigir isso?
Não seria ótimo se o npm pudesse simplesmente adicionar um ou dois recursos e eliminar todo esse risco?
Infelizmente, não pode. Esse risco é uma consequência natural do uso de pacotes de código aberto. Isso significa usar muitos pequenos trechos de código escritos por muitas pessoas. Embora isso seja ótimo para a produtividade, essa contribuição coletiva nos torna bastante dependentes dos autores desses pacotes e de suas intenções.
Também é importante observar que esses comportamentos não são exclusivos do npm. A maioria dos outros gerenciadores de pacotes permite publicações sem confirmação, e praticamente todos permitem que códigos personalizados de pacotes sejam executados durante a instalação. O único motivo para esse risco ser maior com o npm é o grande número de dependências indiretas utilizadas, o que torna praticamente impossível acompanhar todas manualmente.
Dito isso, gostaria muito que a equipe do npm fizesse algumas coisas para ajudar:
Enviar um e-mail a todos os colaboradores quando uma nova versão do pacote for publicada. Esse deveria ser o comportamento padrão, com a opção de desativar os avisos depois.
Exigir credenciais para executar
npm publish, a menos que seja usado um token de automação gerado explicitamente. Isso tornaria mais seguro o comportamento padrão (usarnpm login). Provavelmente seria uma mudança incompatível, então eu não esperaria que acontecesse antes do npm 4.Se possível, restringir as ações que os scripts de instalação podem executar durante a instalação. Isso não eliminaria o risco, mas tornaria o ataque mais difícil e, portanto, menos provável.
Comece a resolver desafios de capture the flag
Aprenda a resolver desafios de capture the flag assistindo sob demanda ao nosso workshop virtual introdutório.


