ADD ANYTHING HERE OR JUST REMOVE IT…
Blog

Chegamos a um ponto sem retorno no gerenciamento de dependências de software?

Os problemas de segurança da cadeia de fornecimento de software  estão afetando fortemente todo o ecossistema OSS; não passa um dia sem que um incidente de segurança  aconteça, afetando usuários e empresas desatentos com software construído com os padrões modernos de  ultracombinabilidade  feitos de um denso número de dependências externas em múltiplas camadas.

De acordo com a pesquisa realizada pela Sonatype em seu relatório anual  State of Software Supply Chain , os ataques à cadeia de suprimentos têm um aumento médio de  742% ao ano.

Existem várias razões por trás disso, como a maior demanda do mercado por software em todos os setores e o tremendo crescimento do modelo de código aberto; estima-se que 90%  das empresas utilizam código aberto.

Outro aspecto que sustenta esta situação é a descoberta e a evolução de ataques cibernéticos especificamente concebidos para anexar a cadeia de abastecimento, como a Confusão de Dependência, o Typosquatting e o seu Primo-Brandjacking, as Injecções de Código Malicioso e o Protestware.

De acordo com  dados da indústria , o número médio de dependências transitivas (indiretas) para um projeto JavaScript no GitHub é  683.

As dependências continuam sendo um dos mecanismos preferidos para criar e distribuir pacotes maliciosos, e ainda é relativamente fácil forjá-los.

Em fevereiro de 2022, o GitHub  introduziu a autenticação obrigatória de dois fatores para os 100 principais mantenedores de npm  e  o PyPA está trabalhando para reduzir a dependência de setup.py , que é um elemento-chave para como esses ataques podem ser lançados ao mesmo tempo em que promovem  a adoção de 2FA usando um painel público  ( Fonte:  https://www.sonatype.com/state-of-the-software-supply-chain/open-source-supply-demand-security )

Para ver os números em ação, testaremos duas das linguagens mais utilizadas em 2022, que são Javascript e Python, e por fim um projeto baseado em Microsserviços composto por diversas linguagens.

JavaScript 

Indo inicializar uma  base de código Javascript NextJS simples   usando as configurações padrão fornecidas:

❯ npx create-next-app@latest
✔ What is your project named? … supply-chain
✔ Would you like to use TypeScript with this project? … No / Yes
✔ Would you like to use ESLint with this project? … No / Yes
✔ Would you like to use Tailwind CSS with this project? … No / Yes
✔ Would you like to use `src/` directory with this project? … No / Yes
✔ Use App Router (recommended)? … No / Yes
✔ Would you like to customize the default import alias? … No / Yes
Creating a new Next.js app in /Users/paolomainardi/temp/supply-chain.

Using npm.

Initializing project with template: app-tw

Installing dependencies:
- react
- react-dom
- next
- typescript
- @types/react
- @types/node
- @types/react-dom
- tailwindcss
- postcss
- autoprefixer
- eslint
- eslint-config-next

added 344 packages, and audited 345 packages in 4s

127 packages are looking for funding
  run `npm fund` for details

5 moderate severity vulnerabilities

To address all issues, run:
  npm audit fix

Run `npm audit` for details.
Initialized a git repository.

Success! Created supply-chain at /dev/supply-chain

Uma coisa a observar é que um aplicativo recém-instalado já possui  5 vulnerabilidades de gravidade moderada . Essa forma de entregar mensagens é arriscada porque inadvertidamente ensina nosso cérebro a ignorá-las, sejam elas úteis ou não. Na próxima vez que um aviso como esse aparecer, ele poderá passar despercebido. Deveríamos melhorar a forma como apresentamos estas mensagens.

Vamos agora contar as dependências:

❯ tree -d supply-chain/node_modules -L 1 | tail -n 1
298 directories

Para imprimir um Hello World com NextJS, precisamos trazer  298 dependências  com  5 vulnerabilidades conhecidas .

Empacotamos o aplicativo em um contêiner OCI, conforme  explicado aqui no documento oficial :

❯ curl -Lo Dockerfile https://raw.githubusercontent.com/vercel/next.js/canary/examples/with-docker/Dockerfile

❯ # Edit this file according to the documentation to produce a standalone build.
// next.config.js
module.exports = {
  // ... rest of the configuration.
  output: 'standalone',
}

❯ docker build -t supply-chain-next-docker .
[+] Building 33.0s (19/19) FINISHED
=> => naming to docker.io/library/supply-chain-next-docker                                  0.0s

❯ syft supply-chain-next-docker > deps
 ✔ Loaded image
 ✔ Parsed image
 ✔ Cataloged packages      [282 packages]

❯ grep apk deps | wc -l
17

❯ grep npm deps | wc -l
249

# Scan for known vulnerabilities.
❯ grype supply-chain-next-docker
 ✔ Loaded image
 ✔ Parsed image
 ✔ Cataloged packages      [282 packages]
 ✔ Scanning image...       [1 vulnerabilities]
   ├── 0 critical, 0 high, 1 medium, 0 low, 0 negligible
   └── 1 fixed
NAME    INSTALLED  FIXED-IN  TYPE  VULNERABILITY        SEVERITY
semver  7.3.8      7.5.2     npm   GHSA-c2qf-rxjj-qqgw  Medium

Nesse caso, a compilação produzida pelo  NextJS  é um pouco menor em termos de pacotes ( por causa do modo autônomo ), e a imagem base Alpine adiciona apenas 17 pacotes no momento sem nenhuma vulnerabilidade conhecida.

Esta é uma imagem pronta para ser enviada em produção, e conta com  282 pacotes (NPM + Alpine)  sem nenhuma modificação feita por nós; é apenas a estrutura básica.

Os grandes tamanhos de pacotes no NodeJS se devem principalmente à biblioteca padrão JavaScript relativamente pequena e à filosofia inspirada no Unix por trás do NodeJS, conforme explicado nesta  postagem do blog .

A filosofia mencionada às vezes pode levar a distorções, como no caso de pacotes como “left-pad” se tornarem parte de muitos pacotes (mesmo como uma dependência transitiva). Isso quase causou  o colapso da internet  quando o autor decidiu removê-lo devido a uma disputa de nome. Você pode ler mais sobre isso aqui.

Acho que este evento também inspirou este famoso   meme do xkcd :

Imagem mostrando a construção da infraestrutura.  Um pequeno pedaço foi adicionado por alguma pessoa aleatória em Nebraska e tem sido mantido ingratamente desde 2003 e contém toda a infraestrutura digital moderna

Pitão 

É a segunda linguagem mais usada e de crescimento mais rápido, com mais de 22% ano após ano (fonte:  https://github.blog/2023-03-02-why-python-keeps-growing-explained/ ).

Para comparar com Javascript, usarei Django como caso de teste.

❯ cat app/requirements.txt
Django==4.2.2

❯ cat app/Dockerfile
FROM python:3.10-alpine
EXPOSE 8000
WORKDIR /app
COPY requirements.txt /app
RUN pip3 install -r requirements.txt --no-cache-dir
COPY . /app
ENTRYPOINT ["python3"]
CMD ["manage.py", "runserver", "0.0.0.0:8000"]

❯ syft django-web
 ✔ Loaded image
 ✔ Parsed image
 ✔ Cataloged packages      [46 packages]

❯ grep apk deps | wc -l
 38
❯ grep python deps | wc -l
 8
❯ grep binary deps | wc -l
 2

# Scan for known vulnerabilities.
❯ grype django-web
 ✔ Vulnerability DB        [no update available]
 ✔ Loaded image
 ✔ Parsed image
 ✔ Cataloged packages      [46 packages]
 ✔ Scanning image...       [2 vulnerabilities]
   ├── 0 critical, 1 high, 1 medium, 0 low, 0 negligible
   └── 0 fixed

NAME    INSTALLED  FIXED-IN  TYPE    VULNERABILITY   SEVERITY
pip     23.1.2               python  CVE-2018-20225  High
python  3.11.4               binary  CVE-2007-4559   Medium

A situação aqui é melhor; Django tem apenas 8 dependências (pelo menos as descobertas por  Syft ).

Como Python é a melhor linguagem para IA, eu queria tentar adicionar  PyTorch  como uma dependência do projeto:

❯ cat app/requirements.txt
Django==4.2.2
--extra-index-url https://download.pytorch.org/whl/cpu
torch
torchvision
torchaudio

# We need a glibc base image to install pytorch.
❯ cat app/Dockerfile
FROM python:3.11.4
...

❯ grep binary deps | wc -l
 2
❯ grep python deps | wc -l
 35
❯ grep deb deps | wc -l
 429

❯ grype django-web
 ✔ Vulnerability DB        [no update available]
 ✔ Loaded image
 ✔ Parsed image
 ✔ Cataloged packages      [455 packages]
 ✔ Scanning image...       [678 vulnerabilities]
   ├── 1 critical, 41 high, 111 medium, 30 low, 464 negligible (31 unknown)
   └── 3 fixed

Mesmo com o PyTorch e suas dependências, o número de pacotes Python é relativamente pequeno, apenas 35; o que é muito maior agora é o número de pacotes transportados da imagem base do Debian (necessários para usar PyTorch em vez de Alpine), para ser mais preciso, 429  pacotes  com um  número absurdo de vulnerabilidades conhecidas , mesmo que esta imagem seja o  Python estável mais recente Versão 3.11 .

Arquitetura e dependências de microsserviços 

Para este teste, usarei um projeto muito interessante do GCP:  Online Boutique é um aplicativo de demonstração de microsserviços  que  prioriza a nuvem e tem como objetivo principal demonstrar o uso de tecnologias como Kubernetes, GKE, Istio, Stackdriver e gRPC e é composto por 11 microsserviços  em  5 linguagens diferentes  : Go, Node.js, Python, Java, C#

  • Outra suposição importante a ser feita aqui é que este projeto devido à sua natureza é mais otimizado nas partes da nuvem do que nos aspectos de desenvolvimento, como dependências
# Calculate packages and vulnerabilities for each microservice.

DOCKER_IMAGES="gcr.io/google-samples/microservices-demo/emailservice:v0.8.0,
gcr.io/google-samples/microservices-demo/checkoutservice:v0.8.0,
gcr.io/google-samples/microservices-demo/recommendationservice:v0.8.0,
gcr.io/google-samples/microservices-demo/frontend:v0.8.0,
gcr.io/google-samples/microservices-demo/paymentservice:v0.8.0,
gcr.io/google-samples/microservices-demo/productcatalogservice:v0.8.0,
gcr.io/google-samples/microservices-demo/cartservice:v0.8.0,
gcr.io/google-samples/microservices-demo/loadgenerator:v0.8.0,
gcr.io/google-samples/microservices-demo/currencyservice:v0.8.0,
gcr.io/google-samples/microservices-demo/shippingservice:v0.8.0,
gcr.io/google-samples/microservices-demo/adservice:v0.8.0"

for image in $(echo $DOCKER_IMAGES | sed "s/,/ /g")
do
  echo "Scanning image: $image"
  syft $image > /dev/null
  grype $image > /dev/null
done%

Vamos eliminar as vulnerabilidades abaixo das altas e formatar os dados em uma tabela:

Microsserviço Linguagem Pacotes ≥ Altas vulnerabilidades
serviço de e-mail Pitão 152 39
serviço de checkout Ir 52 2
serviço de recomendação Pitão 147 39
front-end Ir 71 8
serviço de pagamento Node.js 626 5
catálogo de produtosserviço Ir 51 5
serviço de carrinho C# 25 0
gerador de carga Python/Gafanhoto 137 33
serviço de moeda Node.js 649 5
serviço de entrega Ir 37 3
serviço de anúncios Java 112 19
TOTAL 2059 158

Aqui podemos ver toda a superfície deste aplicativo baseado em microsserviços, um aglomerado de linguagem de programação e dependências de sistema operacional, para  2.095 pacotes  com  158 vulnerabilidades conhecidas altas e críticas  , números enormes.

Node.js, novamente, é o que mais exige dependências. Em vez disso, o Python tem menos dependências, mas mais vulnerabilidades abertas; Java está muito próximo do Python com os números.

O vencedor claro neste cenário é o C#, apenas 25 bibliotecas agregadas e 0 vulnerabilidades.

Pensamentos finais 

Sonatype resume bem o crescimento ininterrupto do ecossistema OSS:

Gráfico de tabela mostrando estatísticas da cadeia de suprimentos de software, 2022

Os dados mostram que:

  1. O desenvolvimento de código aberto está mais florido do que nunca; OSS venceu .
  2. O crescimento está espalhado por todos os maiores ecossistemas, o que é muito bom para toda a indústria; não existe um ator monopolista.
  3. Um ecossistema médio possui  3,3 milhões de projetos , cada um com uma média de  14 versões lançadas  anualmente, o que é bom porque o projeto é mantido e ruim porque requer manutenção.

Hoje estamos expostos a uma grande complexidade escondida por ferramentas muito poderosas.

Obter uma nova dependência é uma tarefa que exige muito pouco esforço; com um gerenciador de pacotes moderno, podemos montar sistemas operacionais e aplicativos inteiros compostos por centenas de milhares de dependências externas, com apenas um ou poucos comandos fáceis, e isso é francamente poderoso e assustador

Em 1984, Edsger Wybe Dijkstra no artigo “ Sobre a natureza da Ciência da Computação ” disse que  “A simplicidade é uma grande virtude mas requer muito trabalho para alcançá-la e educação para apreciá-la. E para piorar: a complexidade vende melhor”.

Pela minha experiência pessoal, em muitos anos neste setor, um dos maiores inimigos desta indústria são as soluções de tamanho único que podem resolver e corrigir magicamente qualquer problema, em qualquer contexto, no que diz respeito à complexidade do problema, este nunca é o caso, é melhor focar na complexidade do problema.

Por exemplo, se eu precisar enviar um site estático com um monte de HTML e CSS, nunca terei a necessidade de implantá-lo em um cluster Kubernetes; em vez disso, tudo que preciso é de um servidor da web não tão sofisticado.

O mesmo se aplica aos microsserviços, nem sempre é o ajuste certo, mesmo quando o cenário pode sugerir o contrário, há casos importantes como  Istio  ou  Amazon Prime Video.

E agora, voltando à questão deste artigo.

A resposta é sim,  já ultrapassamos o ponto sem volta e isso não é necessariamente uma coisa ruim, pelo contrário, acho que estamos vivendo a era de ouro desta indústria e temos tantas oportunidades como profissionais ou cientistas como nunca antes. .

No entanto, isto também significa que a maioria dos projetos depende de grandes quantidades de código que podem ser vulneráveis ​​ou maliciosos. Isto causa estresse para os desenvolvedores e representa riscos significativos para todo o setor. Em alguns casos, pode até pôr em perigo a segurança nacional, como demonstrado pelo  caso Solarwinds .

Hoje, a criação de um novo produto de software deve levantar algumas questões fundamentais, tais como:

  • Como podemos confiar que alguém diferente de nós sempre poderá atuar como um bom ator?
  • Como podemos ter certeza de que todo o código do qual dependemos é feito exclusivamente para fazer o que afirma?
  • Como podemos ter certeza de que o software não foi adulterado em um dos pontos da  cadeia de abastecimento ?

Em 1984, Ken Thompson, no famoso artigo “ Reflexões sobre a confiança na confiança ”, basicamente fez as mesmas perguntas, e a moral, que considero ainda válida, diz que:

  • “Você não pode confiar em um código que você mesmo não criou totalmente. Nenhuma verificação ou escrutínio no nível da fonte protegerá o uso de código confiável. Até que ponto se deve confiar numa afirmação de que um programa está livre de cavalos de Tróia? Talvez seja mais importante confiar nas pessoas que escreveram o software.”

Embora tenha sido possível conhecer os desenvolvedores que criaram e utilizaram bibliotecas de terceiros no passado, hoje não é tão viável devido ao aumento do número de atores envolvidos. Além disso,  os ataques Protestware  podem representar uma forte ameaça que não pode ser facilmente combatida.

Aprendizado 

Então, aqui estão 8  tarefas práticas :

  1. Não caia na armadilha de usar muitas bibliotecas externas, pense duas vezes se realmente precisar delas, em caso de dúvida pense em um  desastre com o teclado esquerdo  .
  2. Microsserviços vs Monolith  não é escolher o bom ou o ruim, concentre-se no problema, mais simples é sempre melhor.
  3. Use a  estrutura SLSA  para compreender as ameaças modernas à cadeia de fornecimento de software e trabalhar para adotar níveis de segurança definidos, um passo de cada vez.
  4. Automatize o gerenciamento de dependências usando uma ferramenta OSS como  o RenovateBot , é agradável e fácil de integrar.
  5. Abrace a nova era de assinatura de artefatos usando  atestados Sigstore  e  SLSA  para produzir seus artefatos e como um guia para escolher software de terceiros que os adote.
  6. Sempre produza Lista de Materiais de Software (SBOM) para um artefato e finja que também o recebeu de seus fornecedores. (  Padrões SPDX  e  CycloneDX )
  7. Automatize e verifique vulnerabilidades conhecidas em todo o seu conjunto de dependências. É fácil fazer isso ao padronizar os artefatos para contêineres OCI. Usando uma ferramenta como  o Grype .
  8. Use imagens Docker menores, como  Distroless  ou  Chainguard,  quando possível

 

Artigo originalmente publicado por CNCF.IO

DNX Brasil – Soluções cloud-native

Sidebar Scroll To Top
Facebook Instagram YouTube linkedin