Como o CPS1 protege sua organização de ataques supply chain
Resumo executivo
No mês de Setembro de 2025 houve um ataque de supply chain em larga escala que comprometeu mais de 500 pacotes do ecossistema NPM, que são baixados milhões de vezes por semana. O ataque explora a prática comum de computadores usado no desenvolvimento conter credenciais para publicar pacotes, acessar recursos de nuvem e APIs terceiras. Essa brecha foi usada para roubar diversas chaves de acesso, incluindo para os provedores de nuvem mais populares, como Amazon Web Services (AWS), Google Cloud Platform (GCP) e Microsoft Azure.
Neste post, explicamos brevemente o que foi o ataque chamado "Shai Hulud", como ele abusa a existência de credenciais em máquinas para desenvolvimento, e como o uso de Cloud Development Environments (CDEs), em particular o CPS1, protege o processo de desenvolvimento de ataques supply chain.
O que foi o ataque Shai Hulud
Nas últimas semanas, o ataque conhecido como Shai Hulud comprometeu mais de 500 pacotes do ecossistema NPM. O código malicioso explora a existência de credenciais nos computadores de desenvolvedores. Hoje em dia é comum um computador usado para desenvolvimento conter credenciais para publicar pacotes, acessar recursos na nuvem e se comunicar com APIs terceiras.
Como o ataque vasculha credenciais de acesso no computador, o Shai Hulud tem a capacidade de se replicar automaticamente. Ao detectar credenciais para publicar na NPM, o código malicioso descobre quais pacotes a máquina infectada tem acesso e altera esses pacotes, publicando novas versões também comprometidas. Quando um desses novos pacotes comprometidos é baixado em outro computador o ciclo se repete.
Este ataque em particular vasculhou computadores buscando credenciais de acesso para provedores de nuvem como Amazon Web Services (AWS), Google Cloud Platform (GCP) e Microsoft Azure; além de credenciais para usar o seu mecanismo de auto replicação, através de chaves de acesso para o GitHub e a própria NPM. A variedade de credenciais roubadas, no entanto, não se limita à apenas esses serviços, pois parte do ataque usa a ferramenta de código aberto TruffleHog para descobrir credenciais vazadas.
Após a coleta de credenciais, o código malicioso cria um repositório na conta do usuário, com um arquivo com todas as credenciais coletadas. O repositório contém um arquivo para configurar uma esteira com GitHub Actions, que envia as crendenciais encontradas para um webhook cadastrado na ferramenta de desenvolvimento webhook.site.
Olhando de longe, todas essas atividades parecem mundanas no dia a dia do desenvolvimento de software: publicar um pacote, criar um repositório e executar uma esteira de CI que faz uma requisição numa ferramenta de testes. Nada que chame muito a atenção. Não é por acaso que demorou um pouco para identificarem que o ataque e que ele comprometeu tantos pacotes.
Ambientes de desenvolvimento em nuvem (CDE) e ataques de supply chain
Hoje na indústria de desenvolvimento de software a diferença no tratamento entre ambientes de desenvolvimento e ambientes de produção traz alguns riscos. Enquanto os ambientes produtivos adquiriram boas práticas de automação e segurança nos últimos anos, a gerência e manutenção de ambientes de desenvolvimento ainda são tratados como algo secundário, uma tarefa de responsabilidade individual, de baixa visibilidade e feita com menos prioridade, quando sobra tempo.
Com a adoção de um ambiente de desenvolvimento em nuvem (CDE) você traz algumas dessas práticas de ambientes produtivos para o ambiente de desenvolvimento, como o isolamento de ambientes. Seja através do uso de containers, seja através do uso de máquinas virtuais ou outros mecanismos, aplicações executando em ambiente produtivo geralmente não tem acesso aos arquivos e recursos computacionais de outras aplicações. Com uma CDE a mesma lógica se aplica no ambiente de desenvolvimento.
Ao trabalhar no projeto A, o seu ambiente possui as ferramentas e arquivos que competem apenas ao projeto A, não ao projeto B. Isso reduz muito a superfície de ataque e o seu impacto: ao baixar uma biblioteca comprometida no projeto A, o código malicioso terá acesso somente ao que estiver naquele ambiente específico, não a todos os projetos e credenciais que um computador de desenvolvimento geralmente tem acesso.
No Cloud Programming Shell (CPS1), a definição de um ambiente de desenvolvimento é feita de forma declarativa, seja via interface do CPS1 ou API do Kubernetes, através do sistema de Templates. Um ambiente de desenvolvimento sempre é criado a partir de uma template. Quando há alteração em uma template, a interface indica que um ambiente de desenvolvimento está desatulizado e deve ser recriado. Esse fluxo de trabalho ajuda a garantir que o ambiente de desenvolvimento está sempre atualizado, seguindo as práticas alinhadas pelo time e tudo de forma automatizada.
Além disso, você também pode contar com todas as extensões e infraestrutura disponíveis para Kubernetes. É possível, por exemplo, usar Network Policies para criar isolamentos de rede em ambientes de desenvolvimento. Na próxima seção, mostramos um exemplo em que criamos uma política que protegeria um desenvolvedor do ataque Shai Hulud mesmo que o ambiente tivesse credenciais de acesso ao GitHub.
Exemplo de política de rede
Suponha uma pessoa que trabalha com desenvolvimento front-end. Suponha também que o usuário deste exemplo se chama "devfe". Neste exemplo, o usuário "devfe" trabalha apenas em projetos que usam pacotes da NPM e tem seu código fonte armazenado no GitHub. Portanto, no dia a dia, seus ambientes de desenvolvimento devem ser capazes de clonar repositórios do GitHub, baixar pacotes da NPM e resolver DNS (para fazer as chamadas citadas anteriormente).
Vamos criar uma política de rede que libera apenas esses acessos necessários para o trabalho diário do usuário "devfe":
- Acesso SSH/Git ao GitHub
- Acesso HTTP ao registry da NPM
- Acesso ao serviço de DNS do cluster Kubernetes
Note que o usuário não terá acesso à APIs do GitHub, apenas ao serviço de git para operações como clone, pull e push.
O GitHub possui uma API para consultar quais IPs podem ser incluídos em uma allowlist para liberar acesso. Logo, podemos usar um script para começar um arquivo que define nossa NetworkPolicy consultando esse endpoint e usando os endereços para operações de SSH/Git:
#!/bin/bash
json=$(curl -s https://api.github.com/meta)
cidrs=$(echo "$json" | jq -r '.git[]')
cat <<EOF
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
EOF
for cidr in $cidrs; do
if [[ "$cidr" != *:* ]]; then
cat <<EOF
- ipBlock:
cidr: $cidr
EOF
fi
done
Após a lista de IPs do GitHub, podemos adicionar a restrição de portas, no
mesmo objeto to para garantir que apenas requisições de git/ssh sejam
permitidas para esses endereços:
ports:
- protocol: TCP
port: 22
O registry do NPM não tem uma API para listar seus IPs. E as NetworkPolicies,
pelo menos até o Kubernetes 1.34, ainda não suportam usar FQDNs, apenas IPs na
notação CIDR. Podemos, portanto, fazer uma chamada curl verbosa de dentro de
um workspace da CSP1 para ver quais IPs são resolvidos, por exemplo:
curl -vvvv registry.npmjs.org
* Host registry.npmjs.org:80 was resolved.
* IPv6: 2606:4700::6810:23, 2606:4700::6810:123, 2606:4700::6810:1e22, 2606:4700::6810:223, 2606:4700::6810:1b22, 2606:4700::6810:1822, 2606:4700::6810:1c22, 2606:4700::6810:1922, 2606:4700::6810:1d22, 2606:4700::6810:323, 2606:4700::6810:1a22, 2606:4700::6810:1f22
* IPv4: 104.16.27.34, 104.16.26.34, 104.16.30.34, 104.16.3.35, 104.16.2.35, 104.16.24.34, 104.16.0.35, 104.16.31.34, 104.16.25.34, 104.16.28.34, 104.16.29.34, 104.16.1.35
(...)
Com isso temos mais uma lista de IPs para colocar em outro bloco do egress da nossa NetworkPolicy:
- to:
- ipBlock:
cidr: 104.16.0.35/32
- ipBlock:
cidr: 104.16.1.35/32
- ipBlock:
cidr: 104.16.2.35/32
- ipBlock:
cidr: 104.16.3.35/32
- ipBlock:
cidr: 104.16.24.34/32
- ipBlock:
cidr: 104.16.25.34/32
- ipBlock:
cidr: 104.16.26.34/32
- ipBlock:
cidr: 104.16.27.34/32
- ipBlock:
cidr: 104.16.28.34/32
- ipBlock:
cidr: 104.16.29.34/32
- ipBlock:
cidr: 104.16.30.34/32
- ipBlock:
cidr: 104.16.31.34/32
E finalmente, precisamos liberar as consultas de DNS para o serviço de DNS do cluster. Essa configuração pode variar de acordo com a configuração do seu cluster. Aqui está um exemplo:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
O YAML completo, com a política para liberar requisições externas de Git/SSH para o GitHub, requisições para o registry NPM e para o serviço de DNS do cluster deve ser semelhante ao exemplo abaixo:
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 192.30.252.0/22
- ipBlock:
cidr: 185.199.108.0/22
- ipBlock:
cidr: 140.82.112.0/20
- ipBlock:
cidr: 143.55.64.0/20
- ipBlock:
cidr: 20.201.28.151/32
# (...) mais IPs do GitHub
ports:
- protocol: TCP
port: 22
- to:
- ipBlock:
cidr: 104.16.0.35/32
- ipBlock:
cidr: 104.16.1.35/32
- ipBlock:
cidr: 104.16.2.35/32
- ipBlock:
cidr: 104.16.3.35/32
- ipBlock:
cidr: 104.16.24.34/32
- ipBlock:
cidr: 104.16.25.34/32
- ipBlock:
cidr: 104.16.26.34/32
- ipBlock:
cidr: 104.16.27.34/32
- ipBlock:
cidr: 104.16.28.34/32
- ipBlock:
cidr: 104.16.29.34/32
- ipBlock:
cidr: 104.16.30.34/32
- ipBlock:
cidr: 104.16.31.34/32
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
Para aplicar essa política de rede em todos os projetos do usuário "devfe"
basta aplicar a configuração acima no namespace do usuário. Cada usuário no
CPS1 tem seu próprio namespace no Kubernetes. Suponha neste exemplo que seu
namespace é o "u-devfe-tl6hmctz". Os namespaces do CPS1 hoje seguem esse
padrão: u-<nomeusuario>-sufixo, com um prefixo configurável (padrão u-) e um
sufixo gerado aleatoriamente. Para aplicar a política:
kubectl apply -n u-devfe-tl6hmctz -f netpol.yaml
Para validar, crie um workspace com o usuário e tente fazer uma requisição para o NPM. Exemplo com curl:
user@netpoltest-yddzgj:~$ curl --no-progress-meter -m 1 -I https://registry.npmjs.org | head -n 1
HTTP/2 200
Confira também que é possível fazer operações de Git/SSH para o GitHub, mas que não é possível interagir com a API:
user@netpoltest-yddzgj:~$ ssh -T [email protected]
Hi devfe! You've successfully authenticated, but GitHub does not provide shell access.
user@netpoltest-yddzgj:~$ curl --no-progress-meter -m 1 -I https://github.com | head -n 1
curl: (28) Connection timed out after 1000 milliseconds
Você pode testar requisições para outros endereços e validar que elas não vão funcionar.
No Shai Hulud, a transmissão das credenciais vasculhadas depende da criação de um repositório no GitHub através de sua API. Com a configuração demonstrada, apesar de ser possível clonar repositórios e enviar alterações, não é possível criar novos repositórios. Se o ataque tentasse transmitir as credenciais encontradas do ambiente comprometido para o webhook.site de forma direta, sem a criação de um repositório, o ataque também não funcionaria.
Nossa visão e próximos passos
Na CPS1 acreditamos que trazer as boas práticas adquiridas em ambientes de produção para o ambiente de desenvolvimento melhora tanto a produtividade quanto a estabilidade de software desenvolvido. Ajudar a desenvolver software de forma segura, protegendo sua cadeia de produção, faz parte desta visão.
Enquanto já é possível usar o exemplo acima para melhorar a proteção do desenvolvimento, vamos adicionar melhorias no produto para tornar esse processo ainda mais fácil: permitindo editar e visualizar essas políticas através da interface, associá-las às templates e suportar o uso de FQDNs além de IPs. Planejamos também adicionar a visualizção do Software Bill of Materials (SBOM) de uma template, para acelerar a identificação de vulnerabilidades antes de sequer chegar na esteira de CI/CD. Essas medidas ajudarão a se proteger e identificar ataques como o Shai Hulud.
Para saber quando essa e outras melhorias estarão disponíveis, acompanhe nosso blog e nosso changelog.
Fale conosco!
Quer saber mais sobre como o CPS1 melhora a segurança do seu processo desenvolvimento de software? Vamos conversar.
Referências
Construindo uma plataforma de Cloud Development Environment: Visão, Decisões e Lições Aprendidas
Os cofundadores do CPS1, Miguel Di Ciurcio Filho e Oscar Esgalha, participaram de uma conversa aprofundada sobre Cloud Development Environments, onde compartilharam histórias e aprendizados de suas experiências anteriores, revelando como nasceu o CPS1.
CPS1 evolui para Platform Orchestrator Unificado com Cloud Development Environment
O CPS1 evoluiu para um orquestrador de plataforma unificado. Provisione infraestrutura de nuvem e gerencie Cloud Development Environment (CDE) a partir de um fluxo unificado.