Software Supply Chain Failures (défaillances de la chaîne d'approvisionnement logicielle)

- Comprendre qu'un site peut rester vulnérable même si son propre code n'a jamais changé, à cause des bibliothèques tierces (dépendances) qu'il utilise (Software Supply Chain Failures, n°3 du Top 10 de l'OWASP (2025)).
- Apprendre à utiliser
composer auditpour détecter des failles de sécurité déjà publiées dans les dépendances d'un projet PHP.
Un projet PHP moderne n'est presque jamais composé uniquement de code écrit par son équipe : il s'appuie sur des dizaines de bibliothèques externes, elles-mêmes construites sur d'autres bibliothèques. Une seule de ces briques, oubliée et jamais mise à jour, suffit à rendre tout l'édifice vulnérable, sans qu'une seule ligne du code du site n'ait jamais changé.
1. Création du projet vulnérable
Créez un dossier tp-dependances contenant un fichier composer.json, qui installe une ancienne
version d'une bibliothèque très utilisée, guzzlehttp/guzzle (un client HTTP, qui permettrait au
site du club de randonnée d'interroger un service météo externe, par exemple) :
{
"require": {
"guzzlehttp/guzzle": "6.3.0"
},
"config": {
"audit": {
"block-insecure": false
}
}
}
Installez cette dépendance :
composer install
L'option audit.block-insecure à false est nécessaire ici uniquement pour les besoins de ce
TP : les versions récentes de Composer refusent désormais, par défaut, d'installer une
dépendance déjà connue comme vulnérable. Vous venez de découvrir, avant même la première ligne de
ce TP, une protection déjà intégrée à votre outillage ! Nous la désactivons volontairement, le
temps d'observer la faille, exactement comme le ferait un projet démarré avant l'existence de cette
protection, ou sur une version plus ancienne de Composer.
2. Observer ce qui a été réellement installé
Affichez la liste des paquets installés par Composer :
composer show
Question : Le fichier composer.json ne mentionne qu'une seule bibliothèque,
guzzlehttp/guzzle. Pourquoi composer show en affiche-t-il cinq ?
📌 Une réponse
La chaîne d'approvisionnement logicielle, c'est exactement cela : une suite de maillons, où chaque bibliothèque dépend d'autres bibliothèques, parfois sur plusieurs niveaux. Il suffit qu'un seul maillon, même indirect, soit compromis ou obsolète pour que l'ensemble du projet soit concerné.
3. Détecter les failles connues avec composer audit
Composer intègre un outil qui compare les versions installées à une base de données de failles déjà publiées. Lancez-le :
composer audit
Résultat attendu (au moment de la rédaction de cette séance) : Composer affiche une liste de
18 alertes de sécurité, réparties entre guzzlehttp/guzzle (14 alertes) et
guzzlehttp/psr7 (4 alertes), avec pour chacune sa gravité, son identifiant CVE et un lien vers
le détail de la faille.
Ce nombre peut varier dans le temps : de nouvelles failles sont régulièrement découvertes et publiées, y compris pour des versions anciennes, longtemps après leur sortie. C'est précisément pour cela qu'un audit ponctuel ne suffit jamais : un projet non audité depuis plusieurs mois peut être devenu vulnérable entre-temps, sans qu'aucune de ses lignes de code n'ait changé.
Question : Parmi les 18 alertes trouvées, certaines concernent guzzlehttp/psr7, une
dépendance que le développeur n'a jamais demandée directement. Pourquoi est-ce quand même son
problème ?
📌 Une réponse
4. Mettre à jour la dépendance vulnérable
Modifiez composer.json pour demander une version récente et maintenue de la bibliothèque :
{
"require": {
"guzzlehttp/guzzle": "^7.0"
}
}
Mettez à jour, puis relancez l'audit :
composer update
composer audit
Résultat attendu : composer audit affiche désormais No security vulnerability advisories found. : plus aucune faille connue, ni sur guzzlehttp/guzzle, ni sur ses dépendances (qui ont,
elles aussi, été mises à jour automatiquement).
L'installation d'une dépendance n'est que le début : sans processus de mise à jour régulière, une bibliothèque installée aujourd'hui en toute sécurité peut devenir vulnérable dans six mois, sans que personne ne s'en aperçoive si l'audit n'est jamais relancé.
5. Vérifier la provenance d'une dépendance avant de l'installer
Avant d'ajouter une nouvelle dépendance à un projet, il est utile de vérifier qu'elle provient
réellement d'une source fiable. Ouvrez la page de guzzlehttp/guzzle sur
packagist.org, le dépôt officiel des paquets
PHP, et notez :
- son nombre de téléchargements (plusieurs centaines de millions) ;
- la présence d'un lien vers un dépôt de code source public (GitHub) ;
- la date de sa dernière mise à jour.
Question : Un attaquant publie un paquet nommé guzzlehtp/guzzle (notez l'orthographe
légèrement différente), qui contient un code malveillant caché dans une bibliothèque par ailleurs
identique à l'originale. Comment appelle-t-on ce procédé, et à quoi les critères ci-dessus
auraient-ils servi pour s'en protéger ?
📌 Une réponse
Contrairement à une dépendance obsolète (ce TP, étapes 1 à 4), qui peut être corrigée après coup par une simple mise à jour, un paquet malveillant installé par erreur a déjà pu exécuter du code sur votre machine ou votre serveur dès son installation (via les scripts automatiques que Composer peut exécuter). La vigilance, ici, doit intervenir avant l'installation.
6. Ajouter une couche de défense supplémentaire : automatiser l'audit
Se souvenir de lancer composer audit à la main, de temps en temps, n'est pas une protection
fiable : ajoutez-le plutôt directement dans votre liste de tâches automatiques, par exemple un
script verifier.sh lancé avant chaque mise en production :
#!/bin/bash
echo "Vérification des dépendances du projet..."
composer audit || exit 1
echo "Aucune faille connue détectée."
L'option || exit 1 est essentielle : elle arrête le script (et donc, dans un vrai pipeline, le
déploiement) si composer audit détecte la moindre faille.
Un audit automatisé à chaque déploiement ne remplace pas une veille régulière sur les failles de sécurité publiées (bulletins de sécurité, abonnement aux alertes GitHub) : il garantit seulement qu'aucune version déjà connue comme vulnérable ne peut être mise en production par erreur, à un moment précis. Les deux mesures se complètent, comme vu dans les séances précédentes de ce cours.
Notions théoriques
Les Software Supply Chain Failures (« défaillances de la chaîne d'approvisionnement logicielle ») regroupent les failles qui proviennent non pas du code écrit par l'équipe de développement, mais des composants tiers utilisés pour construire, distribuer ou faire fonctionner l'application : bibliothèques, outils de développement, serveurs, systèmes d'exploitation, et même le pipeline qui déploie le site.
Pour l'édition 2025 de son Top 10, l'OWASP indique que cette catégorie a reçu le plus grand nombre de votes en première place lors de sa consultation de la communauté : la moitié des personnes interrogées la classaient comme le risque n°1.
Les principales formes de défaillance de la chaîne d'approvisionnement
- Dépendances obsolètes ou non maintenues, dont les failles de sécurité ont été publiées mais jamais corrigées par une mise à jour (ce TP).
- Dépendances transitives non surveillées : une bibliothèque sûre peut en installer une autre, bien moins sûre, sans que personne ne l'ait explicitement choisie.
- Composants provenant de sources non fiables : un paquet au nom proche d'un paquet légitime (typosquatting), ou téléchargé en dehors des dépôts officiels.
- Absence d'inventaire des composants utilisés, qui empêche de savoir rapidement si le site est concerné lorsqu'une nouvelle faille est publiée sur une bibliothèque donnée.
- Pipeline de déploiement (CI/CD) moins sécurisé que l'application elle-même, qui en devient le maillon faible : voir la séance sur la sécurisation d'un pipeline CI/CD.
- Absence de séparation des rôles, lorsqu'une seule personne peut écrire du code et le mettre en production sans aucune relecture.
Pourquoi cette catégorie est-elle si souvent sous-estimée ?
Une dépendance installée une seule fois, au début d'un projet, continue de fonctionner des années durant sans jamais provoquer d'erreur visible : rien, à l'usage, ne distingue une bibliothèque à jour d'une bibliothèque vulnérable depuis longtemps. C'est tout le contraire d'une injection SQL ou d'un XSS, qui se manifestent par un comportement anormal du site : une dépendance obsolète reste silencieuse, jusqu'au jour où quelqu'un l'exploite.
Comment se protéger des défaillances de la chaîne d'approvisionnement ?
- Tenir un inventaire des composants utilisés (parfois appelé SBOM, Software Bill of Materials, la « liste des ingrédients » du logiciel), y compris les dépendances transitives.
- Auditer régulièrement les dépendances avec des outils comme
composer audit(PHP),npm audit(JavaScript), ou des outils dédiés comme OWASP Dependency-Check. - Mettre à jour rapidement une dépendance dès qu'une faille connue la concerne, plutôt que d'attendre une maintenance planifiée plusieurs mois plus tard.
- N'installer des composants que depuis des dépôts officiels (Packagist pour PHP, npm pour JavaScript), en vérifiant leur popularité et leur historique avant un premier usage.
- Supprimer les dépendances inutilisées, qui élargissent inutilement la surface d'attaque du projet.
- Sécuriser le pipeline de déploiement autant que l'application elle-même, et prévoir qu'aucune modification ne parte en production sans relecture par une autre personne.
Un site protégé contre l'injection SQL, le XSS, le CSRF et le contrôle d'accès défaillant, mais qui exécute une bibliothèque tierce dont une faille critique est publiée depuis plusieurs mois, reste entièrement vulnérable : la sécurité d'une application ne se limite jamais à son propre code, elle englobe tout ce qu'elle exécute.
Test de mémorisation/compréhension
Bonnes pratiques
- Lancer
composer auditrégulièrement, et l'intégrer dans le processus de déploiement. - Mettre à jour les dépendances rapidement dès qu'une faille connue est publiée, sans attendre une maintenance planifiée.
- Tenir un inventaire des composants utilisés, y compris les dépendances transitives.
- N'installer des paquets que depuis des dépôts officiels (Packagist, npm), après avoir vérifié leur popularité et leur historique.
- Supprimer les dépendances inutilisées, pour réduire la surface d'attaque du projet.
- Sécuriser le pipeline de déploiement au même niveau que l'application elle-même.
composer audit ne détecte que les failles déjà publiées et répertoriées. Il ne protège ni
contre une faille non encore découverte, ni contre un paquet malveillant publié sous un nom
trompeur mais jamais encore signalé : la vigilance lors du choix d'une dépendance reste
irremplaçable.
Ce que fait Symfony
Symfony maintient lui-même ses composants à jour et publie des bulletins de sécurité officiels
(symfony.com/security) pour chaque faille découverte dans le
framework. La commande composer audit, utilisée dans ce TP, fonctionne exactement de la même
façon pour un projet Symfony : elle vérifie l'ensemble des composants Symfony installés au même
titre que n'importe quelle autre dépendance.
# Dans un projet Symfony, la même commande s'applique
composer audit
Un projet Symfony reste exposé aux failles de ses dépendances non-Symfony (bibliothèques tierces ajoutées par l'équipe, comme ce TP l'a montré avec Guzzle), même lorsque le cœur du framework est parfaitement à jour. La sécurité du framework ne couvre jamais, à elle seule, l'ensemble de la chaîne d'approvisionnement du projet.