Sécuriser les mises à jour automatiques de dépendances et semver
- Accueil
- Développement, applications & chaîne logicielle
- Sécuriser les mises à jour automatiques de dépendances et semver
Sécuriser les mises à jour automatiques de dépendances et semver
Section intitulée « Sécuriser les mises à jour automatiques de dépendances et semver »Le versionnement sémantique (SemVer, Semantic Versioning) est une promesse de compatibilité faite par un mainteneur, pas une propriété vérifiée par qui que ce soit. Les plages de versions et les robots de mise à jour transforment pourtant cette promesse en autorisation implicite d’exécuter le code du prochain publieur. Le sujet : qui décide qu’une nouvelle version entre, et à quel moment.
Accepter sans regarder chaque nouvelle livraison d’un fournisseur parce que l’étiquette dit « même produit, légère amélioration », c’est laisser le livreur ranger lui-même les cartons dans la cuisine. La plupart du temps, tout va bien. Le jour où le carton contient autre chose, personne ne l’a ouvert.
→ Lire l’explication complète de la marmotte
Ce que SemVer garantit et ce qu’il ne garantit pas
Section intitulée « Ce que SemVer garantit et ce qu’il ne garantit pas »SemVer 2.0.0 découpe une version en MAJEURE.MINEURE.CORRECTIVE : la majeure signale une rupture d’interface, la mineure un ajout compatible, la corrective une correction compatible. C’est tout. Rien ne vérifie la compatibilité, ni l’identité du publieur, et rien n’empêche une version 1.4.2 de contenir un voleur de jetons.
Les gestionnaires de paquets traduisent cette promesse en plages. Sous npm, ^1.2.0 accepte tout jusqu’à <2.0.0, ~1.2.0 tout jusqu’à <1.3.0, et ^0.2.3 se limite à <0.3.0 car la série 0.x n’offre aucune garantie. Cargo applique ^ par défaut. Une plage est donc une délégation : elle autorise d’avance des versions qui n’existent pas encore.
Les incidents publics suivent tous le même schéma. event-stream (2018) a reçu une dépendance malveillante après un transfert de maintenance ; ua-parser-js (2021) a publié trois versions piégées depuis un compte compromis ; colors et node-ipc (2022) ont été sabotés par leurs propres auteurs ; en 2025, un hameçonnage de mainteneur a suffi à pousser des versions correctives piégées de paquets téléchargés des milliards de fois par semaine. Chaque fois, le numéro était parfaitement conforme à SemVer.
Plages de versions, fichiers de verrouillage et reproductibilité
Section intitulée « Plages de versions, fichiers de verrouillage et reproductibilité »La règle tranche selon la nature du projet. Une bibliothèque publie des plages larges, sinon ses consommateurs croulent sous les conflits. Une application ou un service déployé fixe tout : fichier de verrouillage versionné, installation stricte, et chaque changement de version passe par une revue de code comme n’importe quel autre changement.
Le fichier de verrouillage (package-lock.json, Cargo.lock, poetry.lock, go.sum) fige l’arbre complet, dépendances transitives comprises, avec une empreinte d’intégrité par paquet (SHA-512, Secure Hash Algorithm, encodé en SRI, Subresource Integrity, pour npm). Il n’a de valeur que si l’installation le respecte : npm ci échoue si le verrou et le manifeste divergent, alors que npm install réécrit le verrou sans prévenir. Une chaîne d’intégration continue (CI) qui lance npm install n’a pas de verrou, elle a un fichier décoratif.
Deuxième verrou souvent oublié : les scripts d’installation. Un paquet npm peut exécuter du code à postinstall, donc sur le poste du développeur et dans la CI, avant tout test. --ignore-scripts par défaut, avec une liste blanche pour les rares paquets qui compilent du code natif, ferme cette porte.
Robots de mise à jour : le compromis fraîcheur contre exposition
Section intitulée « Robots de mise à jour : le compromis fraîcheur contre exposition »Renovate et Dependabot ouvrent des demandes de fusion (pull requests) à chaque nouvelle version. Le compromis central est là : fusionner vite réduit la fenêtre d’exposition aux vulnérabilités corrigées ; fusionner vite augmente l’exposition aux versions malveillantes, qui sont presque toujours détectées et retirées du registre en quelques heures ou quelques jours. Les deux risques n’ont pas la même forme : une vulnérabilité connue demande encore un exploit adapté au contexte, une version piégée s’exécute à l’installation.
La recommandation : délai de quarantaine de sept jours sur toute nouvelle version, fusion automatique réservée aux correctives et mineures des dépendances de développement si la suite de tests passe, revue humaine pour tout ce qui part en production, validation explicite des majeures. Les correctifs de sécurité contournent la quarantaine sur avis de sécurité, pas par impatience. Un robot qui fusionne seul les dépendances d’exécution ne met pas à jour le projet : il délègue le droit de livrer en production à toute personne qui obtient le mot de passe d’un mainteneur.
Point d’architecture rarement vu : les branches créées par Renovate vivent dans le dépôt lui-même, donc la CI qui les construit reçoit les secrets du dépôt. Une dépendance piégée qui s’exécute pendant npm ci peut les lire. Les exécutions déclenchées par Dependabot sur GitHub reçoivent au contraire un jeton en lecture seule et seulement les secrets dédiés à Dependabot. Dans les deux cas, les jobs qui installent des dépendances non revues ne doivent disposer d’aucun secret de déploiement.
Il faut aussi épingler les actions de CI par empreinte de commit plutôt que par étiquette : en 2025, les étiquettes de l’action tj-actions/changed-files ont été redirigées vers un commit malveillant (CVE, Common Vulnerabilities and Exposures, 2025-30066), et tous les dépôts qui référençaient @v45 l’ont exécuté.
Exemple : politique Renovate avec quarantaine et vérification des signatures
Section intitulée « Exemple : politique Renovate avec quarantaine et vérification des signatures »Configuration à placer à la racine du dépôt sous le nom renovate.json5 (JSON5, variante de JavaScript Object Notation qui accepte les commentaires), suivie des commandes d’installation stricte pour la CI.
{ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": ["config:recommended", "helpers:pinGitHubActionDigests"], // Applications : versions exactes dans le manifeste, en plus du verrou "rangeStrategy": "pin", // Quarantaine : aucune version de moins de 7 jours n'est proposée "minimumReleaseAge": "7 days", "internalChecksFilter": "strict", "packageRules": [ { // Seules les correctives et mineures de développement fusionnent seules "matchDepTypes": ["devDependencies"], "matchUpdateTypes": ["patch", "minor"], "automerge": true }, { // Toute majeure attend une validation explicite dans le tableau de bord "matchUpdateTypes": ["major"], "dependencyDashboardApproval": true } ], // Les avis de sécurité passent outre la quarantaine "vulnerabilityAlerts": { "minimumReleaseAge": null, "labels": ["securite"] }}# Installation stricte : échoue si package.json et le verrou divergentnpm ci --ignore-scripts# Vérifie les signatures du registre et les attestations de provenancenpm audit signaturesnpm audit signatures vérifie la signature du registre et, si elle existe, l’attestation reliant l’archive à un dépôt et à une chaîne de construction : elle prouve d’où vient le paquet, pas qu’il est sain.
Ce que ces contrôles ne garantissent pas, et l’exploitation
Section intitulée « Ce que ces contrôles ne garantissent pas, et l’exploitation »Le verrou garantit la reproductibilité, la quarantaine réduit l’exposition aux publications malveillantes détectées vite, la provenance lie un paquet à sa source. Aucun ne détecte un mainteneur légitime qui publie volontairement du code malveillant avec une provenance parfaite, ni une porte dérobée patiente comme celle de xz-utils (CVE-2024-3094), introduite sur deux ans par un contributeur de confiance. Ce reste relève de l’isolement des constructions, de la réduction du nombre de dépendances et de la détection à l’exécution, sujets traités dans la rubrique développement et côté menaces.
En exploitation : conserver les verrous de chaque version livrée pour répondre en quelques minutes à « cette version tournait-elle en production ? », surveiller le tableau de bord du robot (une file de demandes jamais traitées finit en mise à jour massive, donc en revue bâclée), faire tourner les jetons de publication des paquets internes et exiger l’authentification à deux facteurs sur les comptes de registre.
🐝 Ce qui arrive si on s’en passe
Section intitulée « 🐝 Ce qui arrive si on s’en passe »Sans contrôle des plages de versions ni revue des mises à jour automatiques, une contrainte comme ^1.2.0 accepte toute version mineure ou corrective publiée ensuite : une version piégée, diffusée depuis un compte de mainteneur compromis, entre dans la chaîne de construction dès l’installation suivante, sans qu’aucune modification du dépôt ne le signale. Un outil de mise à jour configuré pour fusionner seul les correctifs transforme cette confiance dans le numéro de version en exécution de code tiers dans l’intégration continue, avec accès aux secrets qui y sont exposés. Les CMS ont industrialisé le même pari : la mise à jour automatique des extensions WordPress installe en production, de nuit, le code d’un inconnu que personne n’a relu. Sans fichier de verrouillage versionné, deux constructions du même commit peuvent embarquer des dépendances différentes. Il devient alors impossible d’établir quelle version tournait en production au moment d’un incident, et le retour à un état sain exige de reconstituer l’arbre de dépendances à la main.
🦫 Explication de la marmotte 🍫
Section intitulée « 🦫 Explication de la marmotte 🍫 »Un logiciel moderne réutilise des centaines de petits morceaux de code écrits par d’autres, appelés dépendances. Chaque morceau porte un numéro en trois parties : le premier change quand le morceau casse ce qui marchait avant, le deuxième quand il ajoute quelque chose, le troisième quand il corrige un défaut. Ce numéro est choisi par l’auteur du morceau, et personne ne vérifie qu’il dit vrai.
Beaucoup de projets acceptent automatiquement toute nouvelle version dont seul le deuxième ou le troisième chiffre change. Si le compte d’un auteur est volé, le voleur publie une nouvelle version avec un numéro normal, et elle est installée partout à la prochaine construction. Certains morceaux exécutent même du code dès leur installation.
Pour se protéger, on enregistre dans un fichier la liste exacte des versions utilisées, avec une empreinte de chacune, et l’installation refuse tout écart. Un programme propose ensuite les nouvelles versions, mais seulement après une semaine d’attente, le temps que les versions piégées soient repérées et retirées. Les changements destinés au service réel sont relus par une personne. Les machines qui construisent le logiciel n’ont pas accès aux mots de passe de production pendant qu’elles installent du code non relu.
Rien de tout cela n’arrête un auteur de confiance qui décide lui-même de tricher.
À lire aussi
- Maîtriser le risque des dépendances transitives (npm, pip, Maven, Go)Comment les dépendances transitives entrent dans un projet, ce qu'elles exposent, et comment les verrouiller, les surveiller et les corriger sans casser la construction.
- Signer commits et artefacts contre quelles attaquesSigner ses commits et ses artefacts : contre quoi, précisément, avec les limites, prérequis et règles d’exploitation à appliquer.
- Images Docker de base et confiance héritéeGérer la généalogie des images de base Docker pour maîtriser la confiance, les vulnérabilités et l'exploitation.
- SBOM pour inventorier les composants réellement exécutésUne SBOM permet d’identifier les composants qui composent réellement un logiciel, à condition de la produire au bon endroit et de l’exploiter.
Pages voisines
La citent
- Maîtriser le risque des dépendances transitives (npm, pip, Maven, Go)
- JavaScript et SQL côté web, ce qu'il faut savoir des deux langages
- Python pour l'automatisation défensive, usages et garde-fous
- Attaques supply chain et protection des dépendances
- Installation locale : guide complet pour déployer un site en local
- La chaîne d'approvisionnement logicielle
Sécuriser les mises à jour automatiques de dépendances et semver
Elle cite
: les deux pages se citent l’une l’autre.
