Principaux enseignements
- Pour les versions fixes, Maven interroge les référentiels dans l'ordre et récupère vos coordonnées dans le premier qui les contient.
- Le fichier POM d'une dépendance peut ajouter des référentiels à votre build, et rien ne l'en empêche par défaut.
- A
mirrorOfCette ligne sécurise le côté Maven, mais votre gestionnaire de référentiels doit tout de même veiller à ce que les référentiels publics ne se trouvent pas dans votre espace de noms.
De nombreuses entreprises utilisant Java disposent d'un settings.xml Personne n’y a touché depuis des années. Ça fonctionne. Les builds aboutissent, la CI est au vert, personne ne l’ouvre. Ça vaut quand même le coup d’y jeter un œil. Ce fichier détermine d’où provient chaque artefact, et ce qu’il encode est souvent plus permissif que l’équipe ne le réalise. Une compilation peut se retrouver à une seule déclaration de dépôt mal placée de résoudre une coordonnée interne hors de l’Internet public. Cette lacune est due à la confusion des dépendances Maven, qui fonctionne différemment de npm et pip. Pour les versions fixes, Maven ne compare pas les numéros de version. Il compare le dépôt que votre compilation interroge en premier.
Pourquoi la gestion des dépendances dans Maven fonctionne différemment de celle de npm et pip

--index-url et --extra-index-url, puis sélectionne la version la plus récente parmi toutes celles qui répondent à cette exigence. Publier internal-utils 99.0.0 est accessible publiquement et prend le pas sur un paquet interne portant le même nom. npm se comporte différemment. Un paquet est résolu à partir d’un seul registre, celui qui est configuré pour sa portée, ou le registre public s’il n’existe aucun mappage de portée. L’attaque npm fonctionne parce que le paquet interne n’a jamais été associé à un registre privé ; le client communique donc uniquement avec le registre public, et non parce que npm agrège les résultats et choisit la version la plus récente. Maven ne fonctionne pas de cette manière pour une version fixe. Il résout la version à partir du graphique des dépendances d'abord, puis recherche exactement celui-là groupId:artifactId:version, la coordonnée GAV. La publication d’une version supérieure n’a en soi aucun effet, car personne ne la demande. Elle interroge les référentiels dans le commande de dépôt, en commençant par les paramètres, puis la hiérarchie POM du projet, puis les fichiers POM de dépendances, et choisit le premier qui contient l'artefact.
| Résolveur | Règle de sélection | Ce dont un attaquant a besoin |
|---|---|---|
| pip | Version la plus élevée parmi tous les index configurés | Un numéro de version plus élevé, une fois que les deux index sont pris en compte |
| npm | Registre configuré pour le périmètre du package, public par défaut | Aucun mappage de portée ni de registre pour le paquet interne, et une publication publique portant le même nom |
| Maven, version corrigée | Premier référentiel de la liste qui contient le GAV exact | Vos coordonnées exactes, ainsi que votre position antérieure |
| Maven, plage de versions | Version la plus récente parmi les métadonnées agrégées | Un numéro de version plus élevé |
| Maven, SNAPSHOT | Les métadonnées associent la version de base à une version compilée horodatée, sous réserve de la politique de commande et de mise à jour. | Publier l'accès à un dépôt interrogé |
Le cas de la version fixe est le plus courant, et c’est celui qui modifie votre procédure de correction. L’ordre a son importance, la correspondance exacte des coordonnées est essentielle, tandis que l’inflation des versions n’a aucune incidence. Si un dépôt public précède votre dépôt interne et que vos coordonnées y figurent, c’est la copie publique qui est retenue. Maven Central complique considérablement cette approche rudimentaire. Publier sous un espace de noms implique de prouver que l’on contrôle le domaine correspondant à l’aide d’un enregistrement DNS de type TXT, ou le compte correspondant pour io.github les espaces de noms de styles, pour que personne ne s'en attribue la paternité com.yourcompany simplement parce que vous ne l’avez jamais enregistré. Le véritable risque se trouve ailleurs : les espaces de noms que vous ne contrôlez pas, ceux dont le domaine associé a expiré discrètement et a été récupéré, les dépôts dont les règles de publication sont moins strictes que celles de Central, et tous les dépôts déclarés par un fichier POM dans votre arborescence. JitPack compile à la demande les projets Git pris en charge, et un fichier POM de dépendances peut l'ajouter à votre ensemble de dépôts sans que vous ayez à le déclarer vous-même. Ses coordonnées dépendent de l'hébergeur et du compte Git ; ce n'est donc pas un champ libre, mais il s'agit d'un dépôt auquel vous n'avez jamais choisi de faire confiance. C'est au niveau des plages de versions et des SNAPSHOT que l'agrégation des versions s'applique. Les plages permettent à Maven de fusionner maven-metadata.xml dans l'ensemble des dépôts et sélectionner la version la plus élevée, ce qui remet sur le tapis le risque de détournement lié à la version. Les SNAPSHOT présentent un risque similaire, car les métadonnées associent la version de base à une compilation horodatée et n'importe quel dépôt de l'ensemble peut modifier cette association. Attention : Les plages de valeurs semblent offrir une certaine souplesse et se comportent comme une dépendance non fixée dont la portée s'étend à plusieurs dépôts. Fixez des versions précises et laissez un gestionnaire de dépôt contrôler les mises à niveau.
Ce que votre fichier settings.xml indique réellement à Maven
À quelques pâtés de maisons de là settings.xml affecter la résolution ou ses informations d'identification. <mirrors> redirige les requêtes, <profiles> peut ajouter des dépôts, <activeProfiles> détermine celles qui s'appliquent, et <servers> assure l'authentification. L'élément essentiel est <mirrors>, car un miroir avec le motif approprié remplace toutes les déclarations de référentiel partout ailleurs. C'est la partie de la correction qui concerne Maven. Il faut encore indiquer au gestionnaire de référentiels de ne pas servir votre espace de noms interne à partir de référentiels publics ; c'est l'objet de la section suivante.
<mirrors>
<mirror>
<id>internal-repo</id>
<name>Internal Repository Manager</name>
<url>https://nexus.example.com/repository/maven-public/</url>
<mirrorOf>*</mirrorOf>
</mirror>
</mirrors>
<mirrorOf>*</mirrorOf> Cela signifie que chaque requête vers le référentiel est redirigée vers cette URL, quelles que soient les déclarations contenues dans le fichier POM. C'est exactement ce que vous souhaitez. Voici ce que contiennent en revanche de nombreux fichiers.
| valeur miroir | Interceptions | Jours d'ouverture |
|---|---|---|
central |
Uniquement Maven Central | Tous les autres dépôts déclarés |
external:* |
Référentiels externes | localhost et file:// dépôts |
*,!jitpack |
Tout sauf JitPack | JitPack, délibérément |
* |
Tout | Rien |
Mise en miroir uniquement central C'est une erreur facile à commettre, car cela semble correct. Elle couvre le référentiel auquel tout le monde pense – et permet à chaque référentiel déclaré dans un POM d'être résolu vers sa propre URL. Découvrez lequel vous utilisez.
mvn help:effective-settings
Cette commande fusionne les paramètres globaux et ceux de l'utilisateur, puis affiche ce que Maven utilise réellement, y compris l'activation du profil. Exécutez-la aussi bien sur un agent d'intégration continue que sur un ordinateur portable, car les deux peuvent présenter des différences.
L'injection de référentiel transitive que personne n'a configurée

mvn help:effective-pom | grep -A 8 '<repository>'
Cela affiche la liste générée par votre propre hiérarchie POM, parents et profils compris. Ce qui n'apparaît pas, ce sont les éléments apportés par les POM de dépendances, car ces référentiels s'ajoutent à l'ensemble au moment de la résolution plutôt que d'être fusionnés dans votre POM. Pour avoir une vue d'ensemble complète, observez une compilation réelle.
mvn -X dependency:resolve 2>&1 | grep -E 'Using mirror|Repositories \(' | sort -u
Les lignes « Repositories » indiquent l'ensemble des référentiels utilisés par Maven pour les dépendances et les plugins, tandis que les lignes « Using mirror » indiquent quelles déclarations ont été redirigées. L'exécution de cette commande peut mettre en évidence des référentiels que personne n'a ajoutés sciemment. Retour à la réalité : Le fait que Maven prenne en compte les référentiels déclarés par vos dépendances ne constitue pas une vulnérabilité, et cela ne changera pas. Cette pratique est antérieure à l'apparition des menaces liées à la chaîne d'approvisionnement, et <mirrorOf>*</mirrorOf> C'est la méthode standard, au niveau des paramètres, pour empêcher ces déclarations d'atteindre leurs URL d'origine. Encore un petit détail. <pluginRepositories> est un bloc distinct de <repositories>, et un miroir avec * couvre les deux cas, alors que les miroirs spécifiques à un référentiel ne le font généralement pas. Les plugins s'exécutent avec un accès complet à la JVM, ce qui leur confère des privilèges supérieurs à ceux de tout autre élément présent dans le classpath de votre environnement d'exécution.
Vérifier ce qui a déjà été mis en cache lors de votre compilation
Le cache est moins rudimentaire que ne le laisse supposer sa réputation. Maven stocke les artefacts dans ~/.m2/repository, et le gestionnaire de référentiels locaux amélioré de Maven Resolver enregistre le référentiel distant d’où provient chaque téléchargement. Si vous demandez cet artefact lors d’une compilation ultérieure où le référentiel d’origine ne fait plus partie du périmètre, Resolver considère la copie mise en cache comme absente et effectue une nouvelle recherche parmi les référentiels qui font désormais partie du périmètre. Ce suivi constitue à la fois un moyen de contrôle et une trace écrite. Maven enregistre ces données d’origine à côté de chaque artefact.
find ~/.m2/repository -name '_remote.repositories' \
-exec grep -L 'internal-repo' {} +
Cette liste répertorie les fichiers de suivi sans mentionner l'identifiant de votre miroir. Considérez-la comme un tri sommaire plutôt que comme une traçabilité par artefact, car un même fichier peut couvrir plusieurs artefacts et les installations locales enregistrent une origine vide. Un identifiant inconnu signifie qu’il a été résolu dans le cadre d’une configuration de dépôt différente ; cela vaut la peine de se pencher sur la question. Pour vos coordonnées les plus importantes, vérifiez si elles sont accessibles publiquement.
curl -s -o /dev/null -w "%{http_code}\n" \
https://repo.maven.apache.org/maven2/com/yourcompany/billing-core/
Une valeur de 200 signifie que Central contient du contenu à l'emplacement correspondant à ce `groupId` et à cet `artifactId`. Si vous vous attendiez à ce que ce contenu soit privé, vérifiez ce qu'il contient et identifiez le détenteur de la revendication d'espace de noms vérifiée. Conseil de pro : programmez des vérifications de ce type pour vos coordonnées internes importantes, et étendez-les aux autres référentiels publics accessibles à vos builds, où les règles relatives aux espaces de noms sont moins strictes. Il est préférable de détecter en priorité toute publication inattendue sous des coordonnées sur lesquelles vous vous appuyez.
Bloquer ses propres coordonnées dans le gestionnaire de référentiels

settings.xml achemine chaque requête via une seule URL, mais ne détermine pas ce que cette URL effectue ensuite. Un gestionnaire chargé d’un groupe comprenant à la fois un référentiel hébergé en interne et un proxy central doit tout de même faire un choix. Deux configurations sont ici importantes. La première concerne l’ordre de recherche. Dans Nexus Repository, les membres du groupe sont recherchés dans l’ordre et la première correspondance l’emporte ; les référentiels internes hébergés sont donc prioritaires par rapport au proxy central. Artifactory résout également les référentiels virtuels dans l’ordre. La priorité aux référentiels internes signifie que vos coordonnées sont résolues en interne même lorsque les mêmes existent publiquement. La seconde concerne le blocage pur et simple de vos propres schémas de coordonnées au niveau du proxy central. Nexus Repository procède ainsi avec règles de routage, Artifactory avec des modèles d'exclusion sur le référentiel distant, ou mieux encore, sur le référentiel virtuel qui l'agrège. Dans tous les cas, une requête pour com/yourcompany/** n'atteint jamais l'Internet public. Ce deuxième contrôle permet de pallier les erreurs. L'ordre de traitement dépend de la bonne configuration : un blocage explicite sur votre espace de noms se solde par un échec.
| Contrôle | En quoi ça ne marche pas ? | Résiste à un réordonnancement de groupe |
|---|---|---|
mirrorOf réglé sur * |
Fermé, les builds plantent de manière spectaculaire | N/A |
| Commandes groupées : priorité aux commandes internes | Ouvre, en silence | Non |
| Règle d'exclusion sur le proxy central | Clôturé, la résolution n'a pas été adoptée | Oui |
| Accès sortant restreint depuis les agents de compilation | Clôturé, la résolution n'a pas été adoptée | Oui |
La règle d’exclusion et la restriction de sortie transforment la politique en mesure coercitive. Des dérives de configuration surviennent, les images de base sont héritées d’une autre équipe, et il y a toujours quelqu’un pour ajouter un référentiel afin de débloquer une mise en production le vendredi. Pour le trafic d’artefacts, appliquez une politique de refus par défaut pour les sorties depuis les agents de build et mettez sur liste blanche le gestionnaire de dépôts approuvé, ce qui empêche Maven d’accéder à des dépôts publics arbitraires. Le blocage d’un seul nom d’hôte Central ne suffit pas, car Maven accède à Central sous plusieurs noms et les fichiers POM de dépendances peuvent introduire des hôtes totalement différents. Le routage et le blocage déterminent d’où proviennent les artefacts. Aucun des deux n’évalue si l’artefact est sûr. Un fichier JAR est le résultat d’une compilation, et rien n’exige qu’il corresponde au dépôt source auquel le projet fait référence. L’analyse ne comble pas cette lacune, car les scanners inspectent ce qui a déjà été déployé. Recompiler des paquets à partir d’une source vérifiée dans un environnement contrôlé vous offre une traçabilité indépendante au lieu de vous fier à un binaire opaque ; et lorsque la compilation en amont est reproductible, vous pouvez comparer directement les résultats.
Gradle dispose de fonctions plus adaptées à cet effet
Gradle gère mieux la portée des référentiels que Maven ; il est donc préférable d'utiliser ses fonctionnalités natives plutôt que de transposer les pratiques de Maven. Le filtrage de contenu permet de limiter les coordonnées qu'un référentiel peut fournir. Placez cette configuration dans dependencyResolutionManagement dans settings.gradle.kts, où ce même bloc peut également empêcher les sous-projets de déclarer le leur.
// settings.gradle.kts
dependencyResolutionManagement {
repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
repositories {
exclusiveContent {
forRepository {
maven { url = uri("https://nexus.example.com/repository/internal/") }
}
filter { includeGroupByRegex("com\\.yourcompany(\\..*)?") }
}
maven { url = uri("https://nexus.example.com/repository/maven-public/") }
}
}
exclusiveContent signifie que votre espace de noms est résolu uniquement à partir du référentiel interne, sans consultation d'aucune autre source. C'est confusion entre les dépendances fermé par construction plutôt que par ordre. Notez l'ancrage de cette expression régulière, car un simple com\.yourcompany.* correspond parfaitement com.yourcompanyevil, précisément le « quasi-accident » qu’il est censé détecter. FAIL_ON_PROJECT_REPOS et fait alors échouer la compilation si un sous-projet déclare la sienne, ce qui permet de centraliser la décision dans un seul fichier. Maven ne dispose pas d’équivalent en une seule ligne, même s’il n’est pas pour autant démuni. Le plugin Enforcer permet de bannedRepositories La règle analyse les dépôts de la session de compilation et peut fonctionner comme une liste blanche. Conseil d'initié : Combiner exclusiveContent avec Gradle vérification des dépendances. Le filtrage détermine quel référentiel peut fournir une coordonnée, tandis que les métadonnées de vérification comparent les artefacts aux sommes de contrôle attendues et, lorsque cette fonctionnalité est activée, aux signatures de confiance. Ensemble, elles assurent le routage et l’intégrité, ce qu’aucune configuration Maven ne fait à elle seule. Les dépendances changeantes, telles que les versions SNAPSHOT, ne sont pas prises en compte par ce processus, ce qui constitue une raison supplémentaire de fixer les versions.
Le fichier settings.xml de Gap ne peut pas être fermé
Tout ce qui précède résout le problème de confusion des dépendances Maven au niveau de la couche de routage : la bonne mirrorOf valeur, classement par groupes, règles d'exclusion, contrôles de sortie. Rien de tout cela ne répond à la question suivante : l'artefact est-il sûr une fois qu'il arrive de la bonne source ? C'est ce que SecureChain est conçu pour cela. Il remplace la résolution publique de Maven Central par une alternative vérifiée et recompilée à partir du code source, en contrôlant chaque composant lors de la résolution des dépendances plutôt que d’analyser les artefacts une fois qu’ils ont été déployés. Les nouvelles vulnérabilités CVE sont corrigées en continu, et la couverture ne s’arrête pas à la fin de vie du produit. Découvrez ce que SecureChain pour Java couvertures, ou demander une démonstration par rapport à votre propre version.
FAQ
Qu'est-ce que la « confusion des dépendances » dans Maven ?
Il s'agit d'une attaque dans laquelle une compilation utilise une coordonnée interne provenant d'un dépôt public au lieu de celle du dépôt interne. Pour les versions fixes, la vulnérabilité provient de l'ordre effectif des dépôts (paramètres, puis hiérarchie POM, puis POM de dépendances), et non d'une inflation des versions.
Maven sélectionne-t-il la version la plus récente parmi tous les référentiels ?
Cela ne s'applique pas aux versions fixes. Maven détermine la version à partir du graphe des dépendances, puis recherche cette coordonnée exacte dans le premier référentiel qui la contient. Les plages et les versions SNAPSHOT font l'objet d'une comparaison entre les différents référentiels ; c'est pourquoi les versions exactes sont plus sûres.
Quelle valeur faut-il attribuer à « mirrorOf » ?
Réglez-le sur *, qui achemine toutes les requêtes relatives au référentiel via votre gestionnaire de référentiels, quelles que soient les déclarations du fichier POM. En le configurant sur central n'intercepte que Maven Central et laisse les référentiels déclarés dans les fichiers POM de projet ou de dépendances se résoudre directement.
Une dépendance peut-elle ajouter un référentiel à ma compilation ?
Oui. Maven prend en compte les référentiels déclarés dans le fichier POM d’une dépendance ; ainsi, une dépendance transitive peut en ajouter un à votre ensemble de résolution. Un miroir configuré pour * empêche Maven de les contacter via leurs URL d'origine.
Comment puis-je vérifier quels dépôts sont utilisés par ma build Maven ?
Exécuter mvn help:effective-settings pour la fusion des paramètres et l'activation du profil, mvn help:effective-pom pour la liste générée par votre hiérarchie POM, et mvn -X dependency:resolve pour voir l'ensemble utilisé dans une construction réelle.
Le chiffrement des mots de passe dans le fichier settings.xml est-il sûr ?
Ce n'est pas un problème pour quiconque est capable de lire le système de fichiers. L'héritage de Maven 3 chiffrement des mots de passe conserve un mot de passe principal sur le disque, accessible à toute personne disposant du fichier de paramètres de sécurité. Maven 4 ajoute un répartiteur modulable capable de récupérer la clé principale à partir d'une variable d'environnement ou d'un agent GPG, bien que toute personne disposant de cette clé continue de déchiffrer chaque valeur dans settings.xml. Dans tous les cas, injectez les identifiants provenant d'un véritable référentiel de secrets dans l'environnement de CI.
