Le réflexe est naturel : avant d'implémenter une feature, chercher si un package NuGet ne le fait pas déjà. Un nom de package arrive (depuis une documentation, une recherche, une réponse d'agent), il correspond au besoin, et l'intégration suit. Ce moment, entre la découverte et l'intégration, est celui où un package conçu pour tromper passe le plus facilement : il n'a pas de CVE au moment de sa distribution, il n'apparaît dans aucun outil de scan, et son nom est choisi pour ne pas déclencher d'alerte.
Cet article ne couvre pas les mécanismes d'attaque eux-mêmes : ils font l'objet d'un article de contextualisation dédié. Il ne couvre pas non plus la dimension organisationnelle de la décision. Celle-ci sera traitée dans un futur article sur la gouvernance des dépendances. Il outille un seul moment : depuis la recherche d'un package jusqu'à la décision de l'intégrer.
Périmètre
Évaluation d'un package inconnu sur nuget.org, accessible publiquement, depuis la
recherche jusqu'à la décision d'intégration, avec les outils standard du développeur
.NET (page nuget.org, CLI dotnet, Package Manager de Visual Studio), complétés
de corroborations externes publiquement accessibles quand un signal l'exige. Les
flux NuGet privés,
l'audit binaire post-intégration et la maintenance des dépendances dans le temps sont
hors périmètre. La réponse finale dépend toujours du contexte : criticité du package,
existence d'alternatives, coût d'une implémentation interne.
Ce que les outils ne font pas
Les outils intégrés à NuGet (NuGetAudit, lock files et Package Source Mapping) détectent des vulnérabilités connues dans des packages déjà intégrés et contrôlent les sources autorisées. Aucun n'évalue si un package inconnu mérite d'être intégré au projet. Les scanners tiers (Socket, ReversingLabs) analysent le catalogue en continu et signalent les packages malveillants à nuget.org pour suppression. Cette détection réduit la fenêtre d'exposition, qui se compte en jours sur les cas documentés, mais ne l'annule pas. Pendant cette fenêtre, l'évaluation au moment du choix est la seule barrière active côté développeur.
C'est cette lacune que la grille ci-dessous cherche à combler : les signaux observables sur nuget.org pour se faire une opinion avant d'intégrer un package.
La grille de signaux
La grille est ordonnée par priorité analytique, du signal le plus discriminant au plus contextuel. Aucun ne tranche seul : c'est leur lecture combinée, mise en regard du contexte du projet, qui permet de construire une opinion. Le processus en fin d'article reprend ces signaux dans l'ordre où ils interviennent concrètement, de la recherche du package à la décision.
| # | Signal | Question à se poser |
|---|---|---|
| 1 | Le nom | Le nom pourrait-il être confondu avec un package officiel ? Par quel chemin est-il arrivé ? |
| 2 | Le publisher / owners | L'identité est-elle cohérente avec ce que le package prétend être ? |
| 3 | Le badge Prefix Reserved | Le package de référence le porte-t-il ? Le candidat l'a-t-il ? |
| 4 | Vulnérabilité et version | La page signale-t-elle une vulnérabilité ? La version visée est-elle la plus récente ? |
| 5 | Le dépôt source | Le dépôt est-il accessible, et le binaire publié en provient-il réellement ? |
| 6 | La licence | Est-elle renseignée et compatible avec le contexte du projet ? |
| 7 | La popularité | La trajectoire des téléchargements est-elle cohérente avec l'histoire du package ? |
| 8 | La signature | Qui assume publiquement et de façon vérifiable la publication ? |
| 9 | La maintenance | Si le mainteneur disparaît demain, quel est l'impact ? |
| 10 | Les dépendances | Que tire le package, et qu'est-ce qui sort de l'ordinaire ? |
Pour situer les signaux sur une page réelle, Newtonsoft.Json sert d'illustration : une bibliothèque de référence, largement adoptée et identifiable, dont la page rassemble tous les éléments de la grille. La section exemple en fin d'article déroule la grille complète sur deux profils synthétiques plus proches du cas courant.
Newtonsoft.Json en version 12.0.3. Les éléments numérotés
renvoient aux signaux de la grille : badge de préfixe réservé, owners identifiables,
bandeau de vulnérabilité sur cette version, indication d'une version plus récente
disponible, project website, source repository et licence MIT. Un package parfaitement
légitime peut être un mauvais choix d'intégration à une version donnée : ici, la 12.0.3
porte une vulnérabilité corrigée dans les versions ultérieures. Capture de juin 2026 ;
la mise en page de nuget.org peut évoluer, les signaux décrits restent identifiables.
1. Le nom du package
Le typosquatting
joue sur des variations ASCII : inversion de termes, ajout de segments courants comme
NetCore ou Common, redondance, lettre substituée. Ces
variations sont détectables visuellement, à condition de connaître la nomenclature
officielle du domaine. Le chemin par lequel le nom est arrivé conditionne donc la
vigilance : un nom issu de la documentation officielle d'un projet n'est pas dans la
même situation qu'un nom sorti des résultats de recherche nuget.org sur un terme
fonctionnel, ou d'une source externe non vérifiée (un chat, un ticket, une réponse
d'agent). Sur Acme.NetCore.Json, le nom est plausible pour un wrapper
JSON : rien d'alarmant isolément, ce qui est précisément le piège.
L'attaque par homoglyphes, une variante historiquement significative, consiste à
substituer un caractère par un autre visuellement identique mais de code différent
: un "о" cyrillique (U+043E) à la place du "o" latin rend Newtоnsoft.Json
indiscernable de Newtonsoft.Json, dans toutes les polices. Ce vecteur a
été exploité dans des attaques documentées, dont le cas
Netherеum.All
(Socket, octobre 2025), rendu possible par une validation des identifiants historiquement plus
permissive que celles de npm et PyPI, elles-mêmes restreintes à l'ASCII. En
juin 2026, NuGet a annoncé (NuGet/Announcements#75)
l'alignement de nuget.org sur ces registres : restriction ASCII sur les identifiants
des nouveaux packages, avec un déploiement par phases à partir du 15 juin 2026. Ce
durcissement ferme le vecteur homoglyphe pour les publications futures. Il ne porte
pas sur le stock : les identifiants non-ASCII déjà publiés restent en place par liste
d'exception, et restent installables.
L'inspection des octets reste donc le contrôle systématique sur tout identifiant
copié depuis une source non vérifiée : la restriction ASCII ne s'applique pas au
stock déjà publié, et rien, dans une ligne d'installation par identifiant et version,
ne distingue un package antérieur au durcissement d'un package récent. La saisie
manuelle sur une disposition latine écarte l'homoglyphe d'elle-même ; le copier-coller
préserve la chaîne exacte, homoglyphe compris. Le contrôle tient en une commande
(Format-Hex en PowerShell sous Windows, hexdump -C sous Linux) :
PS> 'Newtоnsoft.Json' | Format-Hex
Label: String (System.String) <35B53910>
Offset Bytes Ascii
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
------ ----------------------------------------------- -----
0000000000000000 4E 65 77 74 D0 BE 6E 73 6F 66 74 2E 4A 73 6F 6E Newtоnsoft.Json
La séquence d0 be apparaît là où un seul octet 6f serait
attendu : tout octet au-delà de 7f sur un nom censé être latin est une
anomalie immédiatement visible. Les éditeurs récents offrent un premier filet : VS
Code souligne par défaut les caractères confusables avec l'ASCII
(editor.unicodeHighlight.ambiguousCharacters), signal heuristique qui ne
remplace pas le contrôle mais peut le déclencher plus tôt.
Indépendamment des homoglyphes, un comportement du registre reste à connaître : un
package retiré des résultats de recherche
(non répertorié ou délisté)
reste installable dès lors que l'ID et la version exacte sont fournis :
dotnet add package Newtonsoft.Json -v 13.0.3 ne passe pas par le filtre
du listing. Une ligne copiée depuis une source non vérifiée peut donc installer un
package qui n'apparaîtra jamais dans une recherche.
La question : le nom pourrait-il être confondu avec un package officiel du domaine, et par quel chemin est-il arrivé ?
Ce que nuget.org bloque à l'upload, et ce qu'il ne bloque pas
Le code source de la galerie NuGet, publié sur
GitHub, montre qu'un contrôle de typosquatting
(TyposquattingService) existe à l'upload. Le nouvel identifiant est comparé
à une liste de référence construite à partir des packages existants, priorisée
(packages vérifiés d'abord, puis les plus téléchargés) et tronquée à une longueur
configurée. Si l'identifiant est jugé trop proche d'une entrée et que le package en
collision n'appartient pas au même compte, l'upload est refusé.
Deux limites, visibles dans le code. La comparaison est contournée si l'identifiant relève d'un préfixe déjà réservé. Et la liste de référence étant tronquée et priorisée sur la popularité, un package de niche a une probabilité réduite d'y figurer : un typosquat qui l'imite n'a rien à quoi être comparé.
Sa capacité historique à détecter les homoglyphes, elle, reste invérifiable :
l'implémentation de repli du dépôt public ne fait qu'une comparaison stricte après
mise en minuscule, et l'algorithme de production est
chargé comme un
composant additionnel non inclus dans le dépôt public. Le cas
Netherеum.All, publié le 16 octobre 2025, signalé par Socket
le 18 et retiré le 20, est cohérent avec une détection absente ou incomplète à
l'upload. La restriction ASCII de juin 2026 rend la question sans objet pour les
nouveaux identifiants, en la tranchant en amont.
2. Le publisher / owners
Sur nuget.org, les owners sont affichés sur la page du package, sous forme de lien vers le
profil de chaque compte. Ce profil affiche l'ancienneté du compte et ses autres
publications. À observer : le nombre et la nature de ces packages, la cohérence entre
l'identité déclarée et le domaine. Un compte créé il y a trois semaines avec un seul
package qui wrappe une bibliothèque majeure pose des questions différentes d'un compte
avec cinq ans d'historique et une dizaine de packages dans le même domaine. En ligne de
commande, le champ Owners de dotnet package search reste
l'indicateur d'identité le plus exploitable.
Le nom de compte lui-même n'est pas à l'abri de l'imitation. Les noms d'utilisateur nuget.org sont restreints aux caractères ASCII (lettres, chiffres, underscore, point, tiret), comme le définit la règle de validation du code source de la galerie : l'homoglyphe cyrillique ou grec, possible sur un nom de package, ne l'est pas sur un nom de compte. Mais rien n'empêche un compte nommé de façon visuellement proche d'un compte légitime (une lettre substituée, un suffixe ajouté), et le mécanisme documenté par nuget.org pour les litiges de nom est réactif, après création du compte. Un nom de publisher plausible pris seul est donc soumis au même type de doute qu'un nom de package pris seul.
La question : l'identité des owners est-elle cohérente avec ce que le package prétend être, et cette identité est-elle confirmée par autre chose que le nom du compte ?
3. Le badge Prefix Reserved
Le badge "Prefix Reserved" est le signal le plus fiable sur l'identité du publisher. Il garantit que le package qui l'affiche appartient au propriétaire du préfixe réservé : un attaquant ne peut pas publier sous ce préfixe sans en être l'owner validé. C'est pour cette raison que le raccourci de confiance n'est jamais le nom de publisher seul, mais le nom associé au badge : le nom peut être imité (signal 2), le badge confirme qu'il ne l'est pas. En présence d'un package de référence avec badge, tout concurrent sans badge est immédiatement distinguable. Le mécanisme de réservation de préfixe est documenté par Microsoft.
Trois limites. Le badge n'est visible que sur nuget.org et dans le Package Manager de Visual Studio : il n'apparaît pas en CLI. Sur les domaines de niche, il n'existe souvent pas : aucun publisher n'a fait la démarche, et les deux noms se présentent à égalité. Enfin, il confirme l'identité du compte au moment de la réservation, pas l'intégrité de tout ce qui a accès à ce compte dans la durée (voir signal 9).
4. Vulnérabilité connue et fraîcheur de version
La page du package signale directement, via un bandeau en haut de page, si la version consultée porte une vulnérabilité connue et si une version plus récente existe. Un package parfaitement légitime peut porter une vulnérabilité : sur une version ancienne, corrigée depuis, ou sur sa dernière version, pas encore corrigée. La décision d'intégration ne porte donc pas seulement sur le package, mais sur la version visée et l'existence d'un correctif.
Cette vérification au moment du choix est distincte de la surveillance continue dans le
pipeline de build (dotnet list package --vulnerable), qui relève de la
maintenance des dépendances et fait l'objet d'un article séparé. La question ici : la
version que je m'apprête à intégrer est-elle saine et à jour ?
5. Le dépôt source
Pour un package open source, l'absence de dépôt accessible pose une question directe : comment évaluer le code intégré, et comment le réévaluer lors des montées de version ? Un dépôt en 404 arrête l'évaluation. L'absence pure et simple du champ source repository est elle-même un signal : un publisher qui documente son package renseigne généralement ce champ.
Le problème de la déclarativité
Mais un dépôt qui répond n'est pas une garantie : le champ est déclaratif, nuget.org ne vérifie pas que le package publié en provient. Rien n'empêche un package malveillant de pointer vers le vrai dépôt du projet légitime qu'il imite. Deux contrôles complémentaires :
Contrôle 1 : Cohérence versions NuGet et releases du dépôt
L'onglet Versions de la page nuget.org liste les versions publiées avec leur date. Une version publiée sur nuget.org qui ne correspond à aucune release du dépôt déclaré rompt le lien que le champ prétend établir.
Contrôle 2 : Cohérence avec des sources datées indépendantes
Les dates d'historique git sont des données déclarées par le client, modifiables avant envoi : les plateformes d'hébergement les acceptent sans validation. Ce qui est coûteux à falsifier, c'est la corroboration externe : la Wayback Machine sur l'URL du dépôt, des mentions datées antérieures à la création revendiquée, la date de première publication sur nuget.org. Un dépôt qui prétend trois ans d'historique mais dont la première capture date de la semaine dernière pose une question concrète.
La question : l'ancienneté et le contenu revendiqués par le dépôt trouvent-ils une corroboration indépendante du dépôt lui-même ?
Le cas des packages propriétaires
Un SDK commercial n'expose pas son code par nature. L'absence de dépôt n'y est pas un signal suspect : c'est une caractéristique du modèle. Les questions se déplacent vers l'éditeur : est-il identifiable et établi ? Le package est-il signé par un certificat rattaché à l'organisation ? Existe-t-il un support officiel et une politique de versioning documentée ?
6. La licence
La page indique la licence. Son absence est un signal en soi : sans licence explicite, le droit d'utiliser le package dans le projet n'est pas établi. Les questions de compatibilité (GPL dans un projet commercial, changement de licence entre versions) sont à résoudre avant l'intégration, indépendamment de la sécurité : Choose a License et la SPDX License List donnent les repères nécessaires. La question : cette licence est-elle renseignée, stable entre versions, et compatible avec le contexte du projet ?
7. La popularité
Le volume de téléchargements peut être gonflé artificiellement par scripting : le cas
Netherеum.All (cas du signal 1) démontre qu'aucun mécanisme côté
nuget.org ne l'a empêché en pratique. Ce n'est donc pas un indicateur de confiance en
soi, et il n'existe pas de seuil universel : la taille des domaines fonctionnels varie trop pour
qu'un chiffre serve de référence. Ce qui reste exploitable, c'est la trajectoire du
package comparée à sa propre histoire. L'onglet Versions affiche la répartition
des téléchargements par version : une progression répartie dans le temps raconte une
adoption réelle ; un volume concentré sur une version récente, en quelques jours, sans
rapport avec le reste de l'historique, est le signal inverse. C'est ce qui a été observé
sur
Netherеum.All
(homoglyphe cyrillique du package Nethereum, documenté par Socket en octobre 2025) : 11,6
millions de téléchargements générés par scripting en quelques jours, sans historique
antérieur pour les justifier.
Ce cas visait précisément le raccourci mental "beaucoup de téléchargements, donc adoption
suffisante pour se dispenser d'une évaluation", un raccourci que la documentation
officielle encourage en présentant les grands volumes comme la preuve que le package
a fait ses preuves. La lecture de trajectoire est elle-même contournable : le cas
StripeApi.Net
(ReversingLabs, février 2026) a réparti environ 180 000 téléchargements artificiels sur
506 versions, simulant une progression organique. La trajectoire élimine donc le pic
grossier, pas la fabrication patiente : elle reste nécessaire, jamais suffisante seule,
et se recoupe avec la cohérence des versions face aux releases du dépôt (signal 5). Un
volume élevé n'est un raccourci légitime que s'il est cohérent avec la trajectoire
propre du package. La question : cette trajectoire est-elle cohérente avec l'âge du
package et sa communauté visible ?
8. La signature du package
Deux signatures peuvent exister : la signature d'auteur (certificat propre du
développeur) et la signature de dépôt (contre-signature nuget.org à la publication).
Contrairement aux autres signaux, ce détail n'est pas exposé sur la page nuget.org
(constaté en juillet 2026) : il s'observe dans NuGet Package Explorer, section
Digital Signatures (outil présenté plus bas), ou via
dotnet nuget verify sur le .nupkg téléchargé. Ce signal est
faible dans les deux sens : une signature ne garantit pas l'innocuité du code, et son
absence n'est pas une preuve de malveillance. Sa valeur se limite à la traçabilité : un
certificat rattaché à une organisation connue permet de vérifier qui a assumé
publiquement la publication. Il pèse comme confirmation sur un package qui passe déjà
les autres signaux, pas comme critère discriminant. La question : qui assume
publiquement, et de façon vérifiable, la publication de ce package ?
9. Maintenance active
Un package sans maintenance active présente deux risques. D'abord les vulnérabilités : une CVE découverte sur un package abandonné ne sera pas corrigée. Ensuite la prise de contrôle du compte publisher (account takeover) : un mainteneur inactif est une cible, et l'attaquant qui prend le contrôle publie une version compromise en bénéficiant de toute la confiance accumulée par l'historique. Les signaux observables : date de la dernière release (onglet Versions), activité du dépôt, réactivité sur les issues.
Comme pour la popularité, la référence la plus fiable est le rythme propre au package. Un package qui publiait tous les deux mois pendant trois ans et n'a rien publié depuis quatorze mois envoie un signal, indépendamment des cycles de release du reste de l'écosystème. À l'inverse, un package trop récent pour avoir un historique exploitable est un signal en soi : si le besoin n'est pas urgent, différer l'intégration de quelques semaines est une réponse légitime. La question : si le mainteneur disparaît demain, quel est l'impact pour le projet, et ce risque est-il acceptable compte tenu de la criticité de la dépendance ?
10. Les dépendances
Un package peut tirer ses propres dépendances, qui tirent les leurs. Si A dépend de B qui dépend de C, C est une dépendance transitive de A. Beaucoup de packages n'en déclarent aucune, et cette absence se constate en un regard ; c'est le cas contraire qui demande l'examen. Le package évalué peut être sain et tirer une dépendance vulnérable ou malveillante : l'évaluer sans regarder ce qu'il embarque laisse un angle mort.
Avant intégration, l'onglet Dependencies de la page nuget.org, à la version précise, liste les dépendances déclarées sans rien installer. Il ne s'agit pas d'auditer tout le graphe transitif, qui peut être large et profond, mais de repérer ce qui sort de l'ordinaire parmi les dépendances directes : une dépendance exotique ou inconnue mérite le même regard, sa page est à quelques clics. Après intégration, la ligne de commande confirme sur le projet ce que la page a laissé voir avant :
# Graphe complet, dépendances transitives incluses
$ dotnet list package --include-transitive
# Dépendances ayant une vulnérabilité connue
$ dotnet list package --vulnerable --include-transitive
# Comprendre pourquoi un package transitif est présent
$ dotnet nuget why <projet> <packageId>
La question : qu'est-ce que ce package tire, et ai-je regardé ce qui sort de l'ordinaire ?
Inspecter le package sans l'installer
Plusieurs signaux de la grille butent sur la même limite : la page nuget.org décrit le
package, elle ne l'ouvre pas.
NuGet Package
Explorer (open source, .NET Foundation), disponible en version web sur
nuget.info, ouvre le .nupkg lui-même. Le
contenu réel du package (assemblies, fichiers .targets ou
.props, ressources embarquées) est consultable sans installation ni
compilation : le moyen le plus direct de voir une cible MSBuild avant qu'elle ne
s'exécute.
L'outil apporte deux vérifications absentes de la page nuget.org. Sa section Health contrôle, quand le publisher a activé ces options au build, que le binaire est relié au code source déclaré (Source Link) et que la compilation est reproductible (build déterministe) : une réponse partielle à la nature déclarative du champ source repository du signal 5. Sa section Digital Signatures détaille les signatures d'auteur et de dépôt (émetteur, horodatage) du signal 8, non exposées sur la page du package. L'outil fonctionne aussi sur un package non répertorié, dès lors que l'ID et la version sont connus (constaté en juillet 2026).
Ses limites sont affichées par l'outil lui-même : les dépendances ne sont pas contrôlées, et les signaux propres à la galerie (badge, profil des owners, bandeau de vulnérabilité, répartition des téléchargements) restent sur nuget.org. NPE complète la page, il ne la remplace pas.
Le processus, étape par étape
La documentation officielle pose que la meilleure façon d'évaluer un package est de l'essayer, et propose les signaux de la page nuget.org comme un substitut plus rapide. Le processus ci-dessous retient la même conclusion pratique (la page d'abord) pour une raison différente : l'essai suppose un package de bonne foi, ce que l'évaluation cherche justement à établir. Un package peut embarquer des cibles MSBuild importées automatiquement à son ajout et exécutées à la première compilation : essayer un package malveillant, c'est déjà exécuter son code. L'essai vient après la grille, pas avant.
La grille décrit ce qu'il faut observer. Le processus ci-dessous reprend ces signaux dans l'ordre où ils interviennent concrètement. Chaque étape renvoie au signal correspondant entre crochets.
- Copier l'identifiant du package depuis sa source. Conserver la chaîne exacte, sans la ressaisir, pour ne pas masquer un éventuel homoglyphe par une saisie "corrigée".
-
Vérifier que c'est une chaîne ASCII. [1] Inspection des octets avec
hexdump -CouFormat-Hex. -
Rechercher l'identifiant exact sur nuget.org. [1] Un
exact match est attendu. Si plusieurs résultats proches apparaissent, la
comparaison entre eux est en soi un signal : variations de nom, owners différents,
présence ou absence de badge. Une absence totale de résultat sur un ID pourtant
installable signifie que le package est non répertorié ou délisté : c'est une
raison d'arrêter et de comprendre pourquoi avant d'aller plus loin. Pour un
identifiant copié depuis une source non vérifiée et pouvant pointer vers un package
antérieur à la restriction ASCII du registre, l'inspection des octets
(
hexdump -C,Format-Hex) reste le contrôle pertinent. - Ouvrir la page du package. [2, 3, 4, 5, 6, 10] La page rassemble d'un coup, sans rien exécuter : owners, badge de préfixe réservé, bandeau de vulnérabilité, fraîcheur de version, dépôt source, licence, et l'onglet des dépendances déclarées. L'absence d'un champ attendu est elle aussi une information.
- Évaluer les signaux que la page ne tranche pas seule. [5, 7, 8, 9, 10] Cohérence du dépôt avec les releases et des sources datées, trajectoire de téléchargements et de releases propre au package, signature et contenu du package via NuGet Package Explorer, maintenance active, regard sur les dépendances qui sortent de l'ordinaire.
-
Décider, et tracer la décision. Le
template de revue
de dépendance disponible en ressource fournit un cadre pour documenter la
décision dans une pull request. Après ajout,
dotnet list package --vulnerable --include-transitiveconfirme l'absence de vulnérabilité connue dans le graphe.
La bonne question n'est pas "est-ce dangereux ?" mais "suis-je en mesure de défendre cette intégration si elle est questionnée dans six mois ?" Si la réponse est non, c'est suffisant pour chercher une alternative.
Exemple : deux profils de niche face à la grille
Un cas extrême comme Newtonsoft.Json, où tous les signaux convergent, ne
représente pas la situation courante. Le cas difficile est le package de niche : peu de
téléchargements, pas de badge, peu de traces externes. C'est là que la grille doit faire
la différence entre deux profils aux signaux faibles.
Les deux packages ci-dessous sont synthétiques : les noms, chiffres et URLs sont fictifs mais
représentatifs de profils rencontrés sur nuget.org. Ce choix est délibéré : les packages
malveillants documentés ont tous été retirés et ne sont plus vérifiables, et utiliser un
package réel encore accessible reviendrait à associer publiquement un développeur de
bonne foi à un article sur les attaques de
chaîne d'approvisionnement logicielle.
Fabrikam.JsonFlatten et Acme.NetCore.Json occupent le même
domaine fonctionnel, sans badge ni volume notable l'un comme l'autre. Seule remarque
possible avant d'ouvrir leurs pages : le segment NetCore au milieu du
second nom suit un pattern de namespace artificiel, sans rien d'alarmant isolément.
Le reste se lit dans les signaux.
| Signal | Fabrikam.JsonFlatten |
Acme.NetCore.Json |
|---|---|---|
| 1. Nom | ASCII vérifié ; nom descriptif d'une fonction précise ; nomenclature cohérente | ASCII vérifié ; segment NetCore pouvant imiter un namespace officiel |
| 2. Publisher / owners | Compte individuel, 3 ans d'ancienneté, 4 packages dans le même domaine | Compte individuel créé il y a 5 semaines, 1 autre package dans le même domaine |
| 3. Badge Prefix Reserved | Absent | Absent |
| 4. Vulnérabilité / version | Aucun bandeau ; dernière version visée | Aucun bandeau ; dernière version visée |
| 5. Dépôt source | Accessible ; releases alignées sur les versions NuGet ; première capture Wayback cohérente avec l'ancienneté revendiquée | URL renseignée mais en 404 |
| 6. Licence | MIT, stable entre versions | Champ absent |
| 7. Popularité | 4 200 téléchargements répartis sur 9 versions et 3 ans | 340 téléchargements sur 3 versions en 5 semaines |
| 8. Signature | Signature de dépôt uniquement | Signature de dépôt uniquement |
| 9. Maintenance | Releases régulières ; issues traitées sous quelques semaines | Aucun historique exploitable |
| 10. Dépendances | Aucune dépendance directe hors framework | Une dépendance inconnue du même publisher |
Aucun des deux packages n'atteint le niveau de convergence d'un
Newtonsoft.Json, et ce n'est pas ce que la grille demande : ce qui compte
est l'absence de signal contradictoire, pas le nombre de signaux positifs. Sur
Fabrikam.JsonFlatten, les signaux sont faibles mais cohérents entre eux :
l'ancienneté du compte, la trajectoire des téléchargements, le rythme des releases et
les traces externes racontent la même histoire. Les indicateurs sont cohérents. Sur
Acme.NetCore.Json, les lacunes s'accumulent au lieu de se compenser : le
dépôt en 404 empêche de valider le code, l'absence de publisher identifiable laisse la
question de l'imputabilité ouverte, la dépendance inconnue ajoute à la complexité de l'analyse.
La conclusion n'est pas "ce package est malveillant" mais "les questions
que ce profil soulève sont difficiles à résoudre, et le bénéfice justifie-t-il le risque
de les laisser sans réponse ?"
Quand un package est écarté, la suite logique est de chercher une alternative ou d'envisager une implémentation interne si le besoin est circonscrit. Un signalement via nuget.org ne se justifie que si l'analyse révèle des éléments activement suspects : un homoglyphe confirmé, un pic soudain de téléchargements artificiels, du code malveillant. Des métadonnées incomplètes ne suffisent pas.
Limites
- Un package sain au moment de l'évaluation peut devenir malveillant ou vulnérable ensuite. Prise de contrôle du compte publisher, dépendance transitive compromise, version ultérieure compromise, CVE découverte plus tard : les signaux reflètent l'état du package au moment de l'évaluation, pas dans le temps. La surveillance post-intégration est un problème différent.
- Les signaux sont contournables, et leur coût varie. Des dates d'historique git antidatées se fabriquent en quelques heures. Ce qui est réellement coûteux à falsifier, c'est la cohérence avec des sources datées indépendantes du dépôt. La grille élève le coût d'une attaque crédible, elle ne l'élimine pas.
- L'audit binaire reste le dernier recours. Quand l'analyse des signaux ne permet pas de se forger une opinion, ILSpy permet d'inspecter le comportement réel du code compilé. Son coût est élevé : l'audit est à refaire à chaque montée de version.
- Cette grille ne couvre pas les flux NuGet privés. Si un flux privé coexiste avec nuget.org, le vecteur de confusion de dépendances nécessite Package Source Mapping. Hors périmètre ici.
- Sur les domaines de niche, les signaux fiables sont rares. Le badge "Prefix Reserved" n'existe souvent pas, les téléchargements sont naturellement faibles, les traces externes datées sont peu nombreuses, et le contrôle de typosquatting à l'upload ne compare qu'à une liste plafonnée de packages populaires. C'est là que le vecteur d'entrée prend tout son poids : si le nom ne vient pas d'une source faisant autorité, l'ensemble de la grille repose sur des signaux peu discriminants. Une analyse approfondie est souvent nécessaire. Et suivant le modèle de menace, l'implémentation interne ou le rejet explicite avec justification sont des réponses légitimes.
Conclusion
Ce processus couvre le moment de la découverte, avant que le package n'entre dans le projet. C'est une condition nécessaire, pas suffisante : un package sain à l'intégration peut devenir problématique après, et la surveillance dans le temps relève d'une démarche distincte.
Ce que la démarche cherche à produire n'est pas une certitude, mais une opinion documentée et défendable. Dans un contexte professionnel, c'est précisément ce qui est attendu d'une décision d'intégration : pas une garantie d'innocuité, mais la trace d'une démarche rigoureuse.
Cet article décrit une démarche d'évaluation fondée sur des signaux publiquement observables, dans l'état constaté à la date de rédaction. Il ne constitue ni une garantie d'innocuité des packages évalués, ni un conseil juridique ou de sécurité couvrant un contexte particulier. La décision d'intégrer une dépendance et ses conséquences restent de la responsabilité du lecteur et de son organisation.
Ressources
- Package ID prefix reservation : Microsoft Learn : critères et procédure de réservation de préfixe, ce que le badge garantit et ne garantit pas.
- Best practices for a secure software supply chain : Microsoft Learn : NuGetAudit, lock files, Package Source Mapping, signature. Couvre la surveillance post-intégration.
- Rechercher et évaluer des packages NuGet : Microsoft Learn : critères d'évaluation officiels. L'article prend le contre-pied de certains de ses raccourcis, sur les téléchargements et sur l'essai direct notamment.
- Package Source Mapping : Microsoft Learn : pertinent si un flux NuGet privé et nuget.org coexistent.
- Choose a License : implications des licences open source courantes (MIT, Apache, GPL et variantes).
- SPDX License List : identifiants normalisés, utile pour interpréter les licences affichées sur nuget.org.
-
NuGet Package Explorer : dépôt source, .NET Foundation
: inspection du contenu d'un
.nupkgsans installation, vérifications Source Link et build déterministe, détail des signatures. Version web sur nuget.info. - ILSpy : décompilateur .NET open source, pour l'audit binaire quand le code source n'est pas auditable.
-
NuGet Gallery : dépôt source, NuGet/Microsoft
: code source de la galerie nuget.org. Le
TyposquattingServicey est consultable ; l'algorithme de comparaison réellement utilisé en production n'y est pas inclus. - Template de revue de dépendance NuGet : Ordanche Solutions : template Markdown à copier dans une pull request pour documenter la décision.