Risques liés aux dépendances externes
De nos jours, la plupart des logiciels reposent sur des composants externes créés et maintenus par des entités tierces. Ces composants externes, également appelés « composants tiers », peuvent par exemple être des bibliothèques. L’objectif est, la plupart du temps, de déléguer certaines opérations spécifiques à des composants dédiés. Cela facilite la maintenance de l’application principale et permet aux développeurs de se concentrer sur le code apportant les fonctionnalités métier. Le type d’opération réalisé par un composant peut être, par exemple, le traitement d’un format de fichier spécifique, la journalisation, la gestion de formats de données métier (par exemple, SWIFT), etc.
Le risque lié à l'utilisation de composants externes
Il existe trois risques majeurs associés à l’utilisation de composants tiers dans les logiciels :
1️⃣ Le risque de présence de vulnérabilités dans un composant, augmentatn la surface d’attaque du logiciel (info).
2️⃣ L'exposition à une attaque de type « dependency confusion » (info).
3️⃣ La présence de code malveillant dans un composant (info).
Vulnérabilités dans un composant
Un composant peut être affecté par une vulnérabilité. Celle-ci peut être publique et faire l’objet d’un CVE (info) (ce n’est pas toujours le cas). Il est important de garder à l’esprit qu’une vulnérabilité dans un composant n’implique pas nécessairement que le logiciel est exposé à un risque. En effet, il se peut que le code concerné ne soit pas utilisé par le logiciel ou ne soit pas accessible à un attaquant. Une analyse est toujours nécessaire pour valider le niveau d’exposition.
Il existe divers outils propriétaires et open source permettant d’analyser un projet et d’identifier les composants présentant des CVE connus. Par exemple, « OWASP Dependency-Check » (info) peut être utilisé comme point de départ, car il permet aux développeurs de progresser progressivement dans la gestion des composants vulnérables (info).
# Collecter et analyser les dépendances d'un projet Maven Java$ mvn -q dependency:copy-dependencies$ dependency-check.sh --project DEMO --scan target\dependency --format JSON --out odc.json
Exemple de rapport utilisant un script personnalisé :

Exposition à l’attaque dite de « dependency confusion »
Le contexte de cette attaque est le suivant : le résolveur de composants externes (par exemple, Nexus, Artifactory, NPM, etc.) présente un problème de configuration.
Ce problème réside dans le fait qu’il recherche d’abord un composant dans les dépôts externes définis avant de consulter les dépôts internes.
Ainsi, si un attaquant publie un composant censé être uniquement interne dans un dépôt externe défini, alors le résolveur récupérera la version depuis le dépôt externe et utilisera le composant déployé par l’attaquant à la place de celui attendu depuis le dépôt interne.
Des outils comme « confused » (info) ou ce script (info) permettent de tester l’exposition à ce type d’attaque pour certaines technologies. Dans l’exemple suivant, certaines dépendances n’existent pas publiquement, car elles proviennent de développements internes. Par conséquent, si un attaquant crée une dépendance portant ce nom et la publie, le résolveur de composants prendra en priorité la version publique au lieu de la version privée.
$ ./confused -l npm package.jsonIl a été constaté que les paquets suivants ne sont pas disponibles dans les dépôts de paquets publics :[!] eyetool[!] db-middleware
Un examen de la liste des dépendances figurant dans le fichier de paquets doit être effectué afin de vérifier que toutes les dépendances internes apparaissent dans les résultats de l’outil. Si une dépendance interne n’est pas affichée par l’outil, cela signifie qu’elle existe publiquement, ce qui pourrait indiquer qu’une attaque par « confusion de dépendance » a déjà été réalisée ou est en cours.
Présence de code malveillant dans des composants
Ce cas se produit lorsqu’un des composants utilisés par le projet (ou par l’un de ses composants, car un composant peut lui-même avoir des dépendances externes) contient du code malveillant.
Ce code malveillant peut être introduit de manière explicite par le fournisseur du composant ou par une entité tierce à la suite du compromis de la chaîne de développement du composant.
Le code malveillant peut, par exemple, être introduit uniquement dans une version spécifique du composant. Ainsi, un composant peut être sûr (version initiale), devenir malveillant à un moment donné (par exemple, après un compromis), puis redevenir sûr (après la résolution de l’incident de sécurité).
Un tel cas est très complexe à détecter de manière entièrement automatisée. En effet, le fait qu’un composant devienne malveillant nécessite souvent une analyse manuelle, en raison des nombreuses techniques existantes pour dissimuler et exécuter du code. De plus, le nombre de dépendances externes d’un projet, même simple, devient rapidement important en raison de la notion de dépendances imbriquées : il s’agit en réalité d’un arbre à plusieurs niveaux de profondeur.
# Compter les dépendances d'un projet Java utilisant Spring 2.5.8# avec les modules Spring Web + Spring Rest Repositories générés# avec l'outil en ligne : https://start.spring.io/$ mvn -q dependency:copy-dependencies$ ls target/dependency/ | wc -l60
Pour les dépendances chargées dans une application web, certaines mesures d’atténuation peuvent être mises en œuvre. Par exemple, si l’application utilise des dépendances externes hébergées sur un réseau de diffusion de contenu (CDN), la fonctionnalité Subresource Integrity (info) peut être utilisée. La mise en œuvre de cette fonctionnalité ne nécessite que deux étapes.
Tout d’abord, il faut calculer le hachage de la ressource. Cette étape peut être réalisée à l’aide d’une commande bash comme celle-ci. Le résultat sera la valeur de hachage du fichier.
$ cat dependency.js | openssl dgst -sha384 -binary | openssl enc -base64 -AOMZSse+JQ3VM1WaqM54ez2Q60OV3BcteFrqEOBMRbjHPVd0t2ztVNRcwhUg3xcKB
Ensuite, le hachage doit être ajouté dans le code source de la page en utilisant l'attribut integrity, comme montré dans l'exemple suivant :
<script src="https://CDN/dependency.js"integrity="sha384-OMZSse+JQ3VM1WaqM54ez2Q60OV3BcteFrqEOBMRbjHPVd0t2ztVNRcwhUg3xcKB"crossorigin="anonymous"></script>
Avec ce hachage défini dans la balise <script>, le navigateur va charger la ressource, calculer le hachage SHA384 du fichier, puis le comparer à la valeur fournie. Si la ressource ne correspond pas au hachage attendu, elle ne sera pas utilisée par le navigateur.
L’étape cruciale consiste à s’assurer que la ressource d’origine est bien la bonne. Ainsi, en cas de compromission du CDN ou si un attaquant parvient à altérer la ressource, la modification sera détectée par le navigateur, et l’utilisateur restera protégé pendant la navigation (même si certaines fonctionnalités peuvent être altérées en raison de l’absence des éléments fournis par la ressource concernée).
Software Bill of Materials - SBoM
Pour créer et gérer l’inventaire de tous les composants externes utilisés dans l’ensemble des logiciels développés par une entreprise, un inventaire de type « Software Bill of Materials » (info) peut être établi par projet, puis centralisé dans un système commun.
Un SBoM (Software Bill of Materials) est une liste de tous les composants open source et externes présents dans une base de code. Il inclut également les licences qui régissent ces composants, les versions utilisées dans le code, ainsi que leur statut de mise à jour (patch).
Les outils proposés par le projet « OWASP CycloneDX » (info) peuvent être utilisés pour générer l’inventaire SBoM d’un projet. Plusieurs technologies sont prises en charge :
# Générer le fichier JSON d'inventaire SBoM pour un projet Java Maven$ mvn -q org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom# Liste des composants utilisés$ cat target/bom.json | jq ".components[].name""spring-boot-starter-web""spring-boot-starter""spring-boot""spring-boot-autoconfigure""spring-boot-starter-logging"...
Le projet « OWASP Dependency-Track » peut être utilisé pour centraliser l’inventaire SBoM de tous les projets (info).
Nouveau risque émergent : sabotage intentionnel de composants
Vers janvier 2022, un nouveau risque est apparu sous la forme de sabotage. En effet, des mainteneurs de composants open source ont publié des versions corrompues pour protester contre l’utilisation de ces composants gratuits et ouverts dans des logiciels commerciaux ou propriétaires (info 1 ; info 2 ; info 3). Ces versions corrompues provoquent des dysfonctionnements dans les applications qui les utilisent.
L’exposition à ce type de risque peut survenir lors de la mise à jour de composants, par exemple à la suite d’une mise à jour d’un framework qui dépend de composants affectés par une opération de sabotage.
Pour détecter une telle exposition, il est possible d’ajouter une pipeline (info) d’intégration continue dédiée, qui compile et exécute les suites de tests unitaires du projet contre les dernières versions de tous les composants externes utilisés. La plupart des systèmes de gestion de dépendances comme Maven, NuGet, npm, PyPI, etc., prennent en charge une syntaxe permettant de référencer la dernière version d’un composant.
Cette pipeline dédiée est distincte de celle qui valide la posture de sécurité de l’application. En effet, dans cette pipeline spécifique, les versions des composants ne sont pas figées : la dernière version disponible de chaque composant est toujours utilisée.
Exemple de syntaxe pour Maven dans le descripteur de projet pour une pipeline de détection de composants corrompus :

Conclusion
Étant donné que les dépendances externes sont présentes dans la majorité des développements internes, ainsi que dans les logiciels open source ou propriétaires, la capacité à les lister, les gérer, détecter leurs vulnérabilités et les mettre à jour devient un point critique du Software Development Life Cycle (SDLC).
Auteurs
Valentin Giannini
Dominique Righetto