Verrouillez vos dépendances pour empêcher les attaques sur supply chain

Partager
Verrouillez vos dépendances pour empêcher les attaques sur supply chain
Photo by Mika Baumeister / Unsplash

Si vous avez suivi l’actualité récente, une nouvelle attaque de type supply-chain a affecté des versions récentes de plusieurs packages TanStack.

Cet article propose une approche simple pour réduire la probabilité et l’impact de ce type de faille de sécurité dans vos applications.

Sommaire


Qu’est-ce qu’une attaque de type supply chain ?

Un package logiciel dépend de plusieurs éléments tout au long de son cycle de vie :

Développeur -> Ordinateur -> Dépôt GitHub -> Registre de packages (NPM)

Une attaque de type supply chain se produit lorsqu’un de ces éléments est compromis, permettant à du code malveillant de se propager à travers le reste de la chaîne.

En pratique, cela signifie souvent qu’un attaquant parvient à injecter du code malveillant dans un package dont dépendent de nombreux autres projets.

Que s’est-il passé ?

Un attaquant a soumis une pull request malveillante ciblant l’un des packages TanStack.

La pull request contenait du code JavaScript malveillant dissimulé. Lorsque la CI s'est executée, un workflow GitHub Action s’est exécuté et l’attaque a empoisonné le cache GitHub Actions.

Le cache ayant été infecté, le code malveillant a ensuite pu capturer des identifiants sensibles, y compris des tokens GitHub.

Grâce à ces identifiants, l’attaquant a obtenu des permissions élevées et a publié des versions compromises des packages sur NPM.

Résultat : les développeurs installant les versions affectées ont téléchargé du code malveillant dans leurs projets sans le savoir.

Pourquoi d’autres projets ont-ils été affectés

Par défaut, les projets Node.js autorisent généralement les mises à jour de dépendances au sein de la même version majeure.

Par exemple :

npm install solid-js

Cela produit l’entrée suivante dans package.json :

{
  "dependencies": {
    "solid-js": "^1.9.5"
  }
}

Le caractère ^ signifie :

Autoriser automatiquement les futures mises à jour mineures et correctives.

Ainsi, même si votre projet utilisait initialement 1.9.5, une réinstallation ultérieure des dépendances peut installer une version plus récente comme 1.11.2.

Cela peut se produire dans de nombreuses situations :

  • Un nouveau développeur rejoint l’équipe et installe les dépendances
  • Un pipeline CI/CD exécute npm install
  • Une GitHub Action lance des tests automatisés
  • Vous réinstallez les dépendances après avoir supprimé node_modules
  • Vous exécutez npm update

Si une version malveillante est publiée pendant cette fenêtre de temps, votre projet peut la récupérer automatiquement.

Comment le verrouillage des versions réduit les risques

L’objectif est simple :

Si la version X.Y.Z fonctionnait hier, installer exactement cette même version demain.

NPM fournit un flag --save-exact pour cela :

npm install --save-exact solid-js

Cela génère ce résultat dans votre fichier package.json :

{
  "dependencies": {
    "solid-js": "1.9.5"
  }
}

Remarquez que le ^ a disparu.

Désormais, chaque npm install utilisera exactement la même version sauf si vous la modifiez explicitement vous-même.

Vous pouvez également verrouiller manuellement une version spécifique :

npm install --save-exact solid-js@1.8.0 

Cette approche existe dans de nombreux écosystèmes.

PHP

composer require laravel/reverb:1.10.1

Ruby

gem install sidekiq -v 8.1.4

Python

poetry add requests@2.34.0

Limites de cette solution

Comme toujours en programmation, il n’existe pas de solution miracle.

Cette approche implique plusieurs compromis :

  • Vous devez vérifier et mettre à jour les dépendances manuellement
  • Vous devriez exécuter npm outdated régulièrement afin de rester à jour sur les correctifs de sécurité
  • Le verrouillage des dépendances directes ne protège pas totalement contre des dépendances transitives compromises
  • Les vulnérabilités de sécurité peuvent rester inaperçues plus longtemps si les mises à jour sont retardées

Autrement dit, le verrouillage des versions réduit la surface d’attaque, mais n’élimine pas complètement les risques de type supply chain.

Conclusion

Les attaques de type supply chain nécessitent une surveillance active et de bonnes pratiques de sécurité.

Le verrouillage exact des versions n’est pas une solution miracle, mais il aide à réduire l’imprévisibilité et limite le risque d’installer accidentellement de nouvelles versions compromises de packages.

Un avantage supplémentaire est la reproductibilité des builds :

  • les membres de l’équipe utilisent les mêmes versions de dépendances
  • les environnements CI deviennent plus prévisibles
  • le débogage devient plus simple
  • les régressions inattendues liées aux dépendances sont réduites

Cependant, cette approche augmente également la charge de maintenance car les mises à jour de dépendances doivent être examinées plus attentivement.

Pour certaines équipes, ce compromis en vaut la peine. Pour d’autres, cela peut sembler trop restrictif.

L’important est de comprendre les risques et de prendre une décision intentionnelle plutôt que de s’appuyer sur le comportement par défaut du gestionnaire de packages.