15 points par GN⁺ 2025-11-22 | 3 commentaires | Partager sur WhatsApp
  • Le cooldown des dépendances (dependency cooldown) est une mesure de sécurité simple et efficace qui peut atténuer la plupart des attaques de la chaîne d’approvisionnement open source
  • Les attaquants compromettent généralement des projets open source populaires pour diffuser du code malveillant, mais, dans la majorité des cas, la fenêtre d’exposition des attaques est courte, souvent inférieure à une semaine
  • En configurant un cooldown qui impose une attente après la publication d’une nouvelle version (par exemple 7 jours), on peut réduire fortement le risque d’infection lié aux mises à jour automatiques
  • Dependabot, Renovate, pnpm, etc. prennent déjà en charge nativement cette fonctionnalité de cooldown, avec une configuration simple et sans coût supplémentaire
  • Si ce mécanisme est fourni par défaut au niveau des gestionnaires de paquets, il peut contribuer à renforcer la sécurité de la chaîne d’approvisionnement tout en réduisant les alertes inutiles

Structure et problèmes des attaques de la chaîne d’approvisionnement

  • La plupart des attaques de la chaîne d’approvisionnement (supply chain attack) suivent le même schéma
    • l’attaquant obtient un accès en exploitant le vol d’identifiants d’un projet open source populaire ou des vulnérabilités CI/CD
    • il téléverse des modifications malveillantes sur des canaux de distribution (PyPI, npm, etc.)
    • les utilisateurs installent la version infectée à cause des mises à jour automatiques ou d’un verrouillage de version insuffisant
    • un fournisseur de sécurité détecte le problème et émet une alerte, puis le registre de paquets supprime la version concernée
  • L’intervalle entre les étapes (1) et (2) est long, mais les étapes (2) à (5) sont gérées en quelques heures à quelques jours, ce qui laisse une courte période d’activité aux attaquants
  • Fenêtre d’opportunité observée sur des cas majeurs des 18 derniers mois
    • xz-utils : environ 5 semaines
    • Ultralytics : 12 heures (étape 1), 1 heure (étape 2)
    • tj-actions : 3 jours
    • chalk : moins de 12 heures
    • Nx : 4 heures
    • rspack : 1 heure
    • num2words : moins de 12 heures
    • Kong Ingress Controller : environ 10 jours
    • web3.js : 5 heures
  • Parmi ces cas, 8 ont eu une durée d’attaque inférieure à une semaine, et la plupart auraient pu être bloqués par un cooldown

Concept et efficacité du cooldown

  • Le cooldown consiste à retarder l’utilisation d’une nouvelle dépendance pendant une durée définie après sa publication
    • pendant cette période, les fournisseurs de sécurité peuvent détecter si elle est malveillante
  • Avantages
    • c’est une approche empiriquement efficace, capable de bloquer la majorité des attaques à grande échelle
    • la mise en œuvre est très simple et la plupart des outils permettent une configuration gratuite
    • exemple avec Dependabot
      version: 2
      - package-ecosystem: github-actions
        directory: /
        schedule:
          interval: weekly
        cooldown:
          default-days: 7
      
    • cela encourage de bons comportements chez les fournisseurs de sécurité : se concentrer sur une détection rapide plutôt que sur des alertes excessives ou la recherche de visibilité

Conclusion et recommandations

  • Dans 8 cas sur 10, la durée de l’attaque était d’une semaine ou moins, et un cooldown de 7 jours aurait suffi à bloquer la plupart d’entre eux
  • Avec un cooldown de 14 jours, tous les cas sauf xz-utils auraient pu être évités
  • Le cooldown n’est pas une solution parfaite, mais c’est un moyen simple de réduire de 80 à 90 % le risque d’exposition
  • Au-delà de Dependabot et Renovate, il faut aussi améliorer les choses pour que les gestionnaires de paquets intègrent nativement le cooldown par défaut
  • La sécurité de la chaîne d’approvisionnement n’est pas seulement un problème technique, c’est aussi une question de structure de confiance sociale, mais le cooldown reste une mesure d’atténuation réaliste et utile

3 commentaires

 
regentag 2025-11-23

En fait, s’il n’y a pas de problème, je pense qu’il vaut mieux ne pas faire la mise à jour.
Faut-il vraiment appliquer à tout prix une nouvelle version qui ne diffère pas beaucoup de la précédente, en acceptant ce risque ?

 
GN⁺ 2025-11-22
Avis Hacker News
  • Les gens craignent d’être exposés à des vulnérabilités graves s’ils ne mettent pas à jour immédiatement, mais en réalité ce n’est généralement pas le cas
    Beaucoup de logiciels ne sont pas en déploiement continu : ce sont les clients qui installent eux-mêmes les nouvelles versions, souvent à un rythme de plusieurs semaines ou mois
    L’important, c’est la surveillance des dépendances et la vérification des vulnérabilités publiées. Il suffit d’évaluer si le produit est réellement affecté, puis de mettre à jour immédiatement la dépendance concernée uniquement dans ce cas

    • Il manque dans l’écosystème une vraie culture de la mise à jour sélective
      L’idée s’est répandue qu’il faut forcément mettre à jour le jour même dès qu’une nouvelle version sort
      Adopter l’approche « faisons-le maintenant, sinon ce sera plus difficile plus tard » sans examiner les changements réels est inefficace
      Rester en permanence à la pointe des numéros de version peut même être contre-productif pour la sécurité
    • Le problème concret, c’est que les équipes sécurité de nombreuses grandes entreprises imposent une politique de « zero CVE »
      Dans notre entreprise, si le scanner détecte une vulnérabilité critique, il faut mettre à jour sous 7 jours
      Si l’échéance est dépassée, cela devient une non-conformité et déclenche une procédure complexe, donc la plupart des gens mettent simplement tout à jour immédiatement
    • Les gens ont peur des 0-day, mais en pratique la plupart des problèmes proviennent de vulnérabilités non corrigées pendant des centaines de jours
    • Le point clé, c’est de savoir si l’application reçoit des entrées inconnues venant de l’extérieur
      Une application comme un navigateur, qui reçoit beaucoup d’entrées externes, doit être mise à jour souvent, alors qu’une appli météo, dont les entrées sont limitées, est relativement plus sûre
    • La stratégie « mettre à jour seulement quand c’est nécessaire » semble séduisante en théorie, mais en pratique le coût d’évaluer chaque vulnérabilité pour chaque produit est trop élevé
      Il est souvent plus efficace de mettre à jour régulièrement tout en appliquant en parallèle des mesures de défense contre les attaques de supply chain
  • Un modèle de distribution comme Debian stable, où la distribution gère les dépendances communes et effectue une montée de version globale tous les quelques années, paraît de plus en plus raisonnable
    Certains écosystèmes évoluent trop vite, ou disposent de systèmes de packaging par distribution insuffisants
    Par exemple, il reste difficile d’installer des bibliothèques Node.js via apt pour les utiliser dans un projet

    • Il ne faut pas confondre le « mouvement » avec l’« action »
      Un écosystème qui bouge vite sans changement fondamental n’est pas sain
      JS a connu très peu de progrès réels ces 3 dernières années, mais reconstruire aujourd’hui un projet vieux de 3 ans peut quand même être aussi douloureux qu’une réécriture
    • Les résultats de recherche des paquets node dans Debian montrent qu’il en existe certains, mais que ce n’est pas complet
      Dans des distributions comme Arch, il n’y en a parfois pas du tout
    • Avec Rust, il est possible de travailler suffisamment rien qu’avec les dépôts Debian
    • Le modèle des versions stables impose aux applications la charge de devoir prendre en charge en même temps les anciennes et les nouvelles versions
  • Il y a l’idée qu’instaurer une période de cooldown avant les mises à jour de dépendances, pour empêcher les attaques de supply chain, vaut mieux qu’utiliser immédiatement la toute dernière version pour se protéger des 0-day
    La question est de comparer la probabilité qu’une mise à jour introduise une nouvelle vulnérabilité avec celle qu’elle corrige une vulnérabilité existante
    Selon SemVer, les versions de correctif sont relativement sûres, donc on peut aussi envisager une courte période de cooldown selon le type de version
    Par exemple, si l’on passe de 2.3.4 à 2.4.0, et qu’aucune fonctionnalité urgente n’est nécessaire, mieux vaut attendre la sortie de 2.4.1

    • Quand un 0-day rendu public apparaît, une advisory de sécurité est publiée, donc des outils comme Dependabot ignorent le cooldown et appliquent le correctif immédiatement
      La plupart des vulnérabilités ne viennent pas d’attaques intentionnelles, mais de bogues ordinaires
    • Par défaut, on se fonde toujours sur des hypothèses. Quand de nouvelles informations arrivent, on ajuste
  • Une politique limitant le nombre et la complexité des dépendances constitue une approche plus forte
    Au lieu d’ajouter des bibliothèques « qui font tout », il faudrait n’ajouter que des dépendances petites et à l’objectif clair
    Il serait aussi plus simple à gérer si les bibliothèques proposaient des versions LTS ne contenant que des correctifs de sécurité

    • Cet argument revient souvent, mais en pratique réutiliser du code éprouvé est bien plus efficace
      Réimplémenter soi-même peut être du gaspillage. J’aimerais voir des exemples concrets de bibliothèques problématiques « everything library »
    • La plupart des attaques de supply chain se produisent sur la surface d’attaque liée à l’ingénierie sociale
      Si l’on fait confiance au même développeur à travers plusieurs bibliothèques, les relations de confiance comptent davantage que le nombre de paquets
      La complexité est corrélée aux vulnérabilités, mais n’en est pas la cause directe
    • Avec l’arrivée des outils d’IA, le coût d’implémenter soi-même des dépendances non essentielles diminue
      Il devient désormais possible de faire des choix tenant compte de la maintenance à long terme plutôt que de la seule vitesse à court terme
    • Des dépendances trop finement découpées peuvent au contraire augmenter leur nombre et la charge de build
    • Il est difficile de justifier qu’une bibliothèque de bas niveau ait elle-même d’autres dépendances
      Dans le monde du C++, c’est même parfois un argument concurrentiel
  • La pression pour mettre à jour systématiquement vers la dernière version vient d’une croyance erronée selon laquelle le logiciel s’améliore toujours
    En réalité, cela peut simplement remplacer des bogues connus par de nouveaux bogues inconnus
    Surveiller les problèmes publics et ne corriger que lorsque c’est nécessaire est une approche raisonnable

    • Avec log4shell par exemple, log4j 1.x n’était pas vulnérable, alors que le bogue a été introduit dans la 2.x
      C’est donc un cas où une ancienne version était en fait plus sûre
    • Autrefois, l’intervalle entre les releases était plus long et les améliorations de qualité plus nettes ; aujourd’hui, il s’agit surtout de changements périphériques
      C’est peut-être aussi parce que nous utilisons déjà des logiciels suffisamment stables
  • Si tout le monde dit « attendons un peu », alors plus personne ne validera rien en premier, un peu comme dans une tragédie des communs
    Plus on attend, plus la dette technique s’accumule ; il faut donc des mises à jour progressives et des mesures d’atténuation comme le zero trust et la surveillance

    • Attendre environ une semaine après la publication d’une nouvelle version ne crée pas une dette technique énorme
      Entre-temps, les scanners de sécurité auront déjà détecté les vulnérabilités
    • Les récentes attaques de supply chain ont souvent été détectées non pas via l’exposition des consommateurs, mais parce qu’elles laissaient du temps de réaction aux mainteneurs
    • Même si le nombre de consommateurs diminue, cela donne aux chercheurs et aux mainteneurs le temps d’analyser
      Si un attaquant publie une release non autorisée, on peut parfois le repérer immédiatement
    • Les grands systèmes procèdent de toute façon par déploiement progressif, donc une mise à jour immédiate à l’échelle globale est impossible
  • L’idée du cooldown est bonne, mais il existe un risque qu’un attaquant l’exploite pour créer une fausse urgence
    En présentant une version comme un « correctif de sécurité urgent », il peut pousser à une installation anticipée alors que cette version est en réalité malveillante
    Il faut se préparer à ce type d’attaque par pression psychologique

    • Si l’attaquant inclut aussi une correction de bogue dans l’exploit qu’il diffuse, il peut inciter les développeurs à contourner le cooldown et à l’installer
    • La question se pose alors : « Comment créer un tel bruit ? » — autrement dit, comment un attaquant pourrait manipuler l’opinion
  • Une autre raison d’avoir un cooldown est de laisser le temps aux mainteneurs de se rendre compte eux-mêmes d’une compromission
    Les attaquants visent les moments où les mainteneurs sont absents — vacances, conférences, jours fériés, etc.
    Beaucoup de projets sont gérés par une ou deux personnes, donc ce décalage temporel est crucial

  • Tout dépend de la nature du projet et de sa surface d’attaque
    Il faut en finir avec l’époque où l’on se contentait de suivre des « bonnes pratiques »
    La sécurité est comme un sport de contact : il faut réfléchir chaque jour de façon critique aux détails

  • Il y a aussi une limite du cooldown si l’on considère qu’avec le temps tout devient automatiquement plus sûr
    Si personne ne regarde le code, une semaine plus tard le risque reste le même
    À la place, une approche de déploiement progressif (gradual rollout) peut être efficace
    Chaque consommateur définit un facteur de délai, de sorte que ceux qui tolèrent le plus le risque rencontrent les problèmes en premier,
    tandis que les autres restent protégés pendant ce temps
    Les mises à jour risquées sont retirées de la file, et les consommateurs retardés ne les voient même jamais

 
aer0700 2025-11-23

Ces derniers temps, je me demande parfois ce qui est le plus supportable : simplement réinventer la roue, ou gérer un Tetris de dépendances.
Si quelque chose tourne mal dans for while, il suffit de corriger mon code ; mais dans un Tetris de dépendances, quand une des roues se met soudainement de travers, c’est beaucoup plus difficile à déboguer.