Aller au contenu principal

Développement Web

Contrer les attaques par confusion de dépendances : sécuriser l'arbre Composer de vos projets Drupal

EN BREF

  • La confusion de dépendances permet à un attaquant d'exécuter du code malveillant sur vos serveurs en publiant un paquet public portant le même nom que votre module Drupal privé.
  • Le durcissement du fichier composer.json par l'activation de dépôts canoniques et l'exclusion de namespaces spécifiques constitue la première ligne de défense indispensable.
  • L'automatisation des audits via l'intégration continue bloque systématiquement toute tentative d'injection avant le déploiement sur vos infrastructures AWS ou OVHcloud.

La sécurité des applications web modernes ne repose plus uniquement sur la robustesse du code écrit en interne. Elle dépend de manière critique de l'intégrité de l'arbre des dépendances tierces. Dans l'écosystème Drupal, l'adoption généralisée de Composer a révolutionné la gestion des modules et des bibliothèques. Cependant, cette flexibilité introduit un vecteur d'attaque particulièrement redoutable : la confusion de dépendances, ou Dependency Confusion.

Ce guide technique détaille le fonctionnement de cette menace au sein des projets Drupal d'entreprise et fournit des solutions concrètes pour verrouiller vos processus d'intégration continue et vos serveurs de production.

Schéma technique illustrant le détournement d'une requête de dépendance privée vers le registre public Packagist

Comprendre le mécanisme de l'attaque par confusion de dépendances

Pour contrer efficacement une menace, il est nécessaire de comprendre précisément comment elle s'opère. Dans le cadre de la confusion de dépendances, l'attaquant exploite le comportement par défaut des gestionnaires de paquets.

Le rôle de l'algorithme de résolution de Composer

Par défaut, lorsqu'un projet nécessite un paquet, Composer interroge l'ensemble des dépôts déclarés dans le fichier de configuration. Si un même paquet est présent sur plusieurs dépôts (par exemple, sur un dépôt Gitlab privé et sur le registre public Packagist), Composer applique un algorithme de résolution basé principalement sur la version.

L'outil privilégie systématiquement la version la plus élevée disponible, quelle que soit la source. Si un attaquant parvient à identifier le nom d'un module privé utilisé par votre entreprise et l'enregistre sur Packagist avec un numéro de version très élevé, Composer téléchargera la version malveillante publique lors de la prochaine mise à jour.

Le cas d'usage typique d'un module Drupal personnalisé

Prenons l'exemple d'une grande entreprise qui développe un module Drupal sur mesure nommé mon-agence/drupal-custom-auth. Ce module est stocké sur un dépôt Gitlab interne et configuré dans le projet d'entreprise.

Un attaquant externe analyse les fichiers de configuration publics (parfois exposés par erreur ou présents dans des dépôts de code ouverts) et découvre ce nom de paquet. Il publie alors sur Packagist un script malveillant sous le nom exact mon-agence/drupal-custom-auth avec la version 999.0.0. Lors du prochain build d'intégration continue, Composer interrogera Packagist, constatera que la version 999.0.0 est supérieure à votre version locale et installera le code de l'attaquant sur votre serveur d'intégration ou de production.

Durcir la configuration du fichier composer.json

La méthode la plus efficace pour bloquer cette attaque consiste à modifier la manière dont Composer interroge et priorise vos dépôts de paquets.

Configurer des dépôts privés canoniques

Par défaut, les dépôts déclarés dans Composer ne sont pas canoniques. Cela signifie que Composer continuera à chercher un paquet sur Packagist même s'il l'a déjà trouvé dans votre dépôt privé. Pour corriger cela, il faut déclarer explicitement votre dépôt privé comme étant canonique.

Voici un exemple de configuration sécurisée à intégrer dans votre fichier de configuration :

{
    "name": "mon-agence/projet-drupal",
    "description": "projet Drupal securise d entreprise",
    "repositories": [
        {
            "type": "composer",
            "url": "[https://composer.mon-registre-prive.fr](https://composer.mon-registre-prive.fr)",
            "canonical": true
        }
    ],
    "require": {
        "php": ">=8.2",
        "drupal/core-recommended": "^10.1",
        "mon-agence/drupal-custom-auth": "^1.0"
    },
    "config": {
        "allow-plugins": {
            "composer/installers": true,
            "drupal/core-composer-scaffold": true
        }
    }
}

En ajoutant l'instruction "canonical": true sur votre dépôt privé, vous indiquez à Composer que si le paquet demandé est présent sur ce dépôt, il ne doit sous aucun prétexte chercher des versions alternatives sur d'autres registres comme Packagist.

Utiliser les filtres d'exclusion de paquets

Si vous utilisez des dépôts tiers ou des répertoires de types différents, vous pouvez configurer des règles strictes de filtrage. Cela permet d'interdire formellement à Packagist de répondre aux requêtes concernant les namespaces internes de votre organisation.

Voici comment structurer vos exclusions pour protéger vos modules spécifiques :

{
    "repositories": [
        {
            "type": "composer",
            "url": "[https://packages.drupal.org/8](https://packages.drupal.org/8)",
            "exclude": [
                "mon-agence/drupal-custom-auth",
                "mon-agence/drupal-custom-theme"
            ]
        },
        {
            "type": "composer",
            "url": "[https://repo.packagist.org](https://repo.packagist.org)",
            "exclude": [
                "mon-agence/drupal-custom-auth",
                "mon-agence/drupal-custom-theme"
            ]
        }
    ]
}

Cette approche garantit que les requêtes concernant vos paquets internes ne quitteront jamais votre réseau local ou vos serveurs de build privés, empêchant ainsi toute interception ou substitution.

Capture d'écran d'un terminal montrant le blocage d'une tentative de téléchargement d'un paquet exclu

Industrialisation et automatiser l'audit en intégration continue

La sécurisation locale du fichier de configuration ne suffit pas : elle doit être validée et imposée à chaque étape du cycle de vie logiciel via vos pipelines de déploiement.

Intégrer l'audit de sécurité dans les pipelines CI-CD

L'outil de build doit exécuter des vérifications systématiques avant d'autoriser tout déploiement sur vos serveurs cibles, qu'ils soient hébergés chez AWS ou OVHcloud.

Voici un exemple de configuration de pipeline d'intégration continue sous Debian pour valider l'arbre des dépendances :

# Pipeline de validation de securite Composer
stages:
  - test
  - security

validate_composer:
  stage: test
  image: composer:2.6
  script:
    // Verifier que le fichier lock est parfaitement synchrone avec le json
    - composer validate --no-check-publish --strict
    // Verifier l absence de dependances non securisees connues
    - composer audit --format=json

audit_dependencies:
  stage: security
  image: debian:bookworm-slim
  before_script:
    - apt-get update && apt-get install -y php-cli unzip curl
    - curl -sS [https://getcomposer.org/installer](https://getcomposer.org/installer) | php -- --install-dir=/usr/local/bin --filename=composer
  script:
    // Analyse approfondie des dependances du projet
    - composer show --locked

Cette configuration de pipeline garantit que toute modification suspecte dans le fichier de verrouillage sera immédiatement détectée et bloquera le déploiement.

Valider l'intégrité des fichiers de verrouillage sous Debian

Dans vos environnements de production sous Debian, l'installation des dépendances ne doit jamais utiliser la commande classique de mise à jour. Utilisez toujours la commande d'installation stricte qui s'appuie uniquement sur le fichier de verrouillage existant.

La commande suivante doit être privilégiée lors de vos déploiements automatisés :

# Executer l installation de production sans interaction et de maniere stricte
composer install --no-dev --optimize-autoloader --no-interaction --frozen-lockfile

Cette commande empêche Composer de recalculer l'arbre des dépendances à la volée sur le serveur de production, éliminant ainsi le risque d'introduire un nouveau paquet non testé.

Comparaison des approches de sécurisation de Composer

Pour vous aider à choisir la meilleure stratégie selon votre infrastructure, voici une analyse comparative des différentes méthodes de protection disponibles :

  • L'utilisation de dépôts canoniques :
    • Niveau de sécurité : excellent.
    • Complexité de mise en œuvre : très faible.
    • Impact sur les performances : négligeable.
    • Recommandation : obligatoire pour tous les projets d'entreprise utilisant des modules internes.
  • Le filtrage par exclusion explicite :
    • Niveau de sécurité : très élevé.
    • Complexité de mise en œuvre : moyenne.
    • Impact sur les performances : aucun.
    • Recommandation : idéale si vous utilisez de nombreux dépôts tiers en plus de Packagist.
  • La désactivation complète de Packagist :
    • Niveau de sécurité : maximal.
    • Complexité de mise en œuvre : élevée.
    • Impact sur les performances : nécessite un proxy local.
    • Recommandation : réservée aux environnements hautement sécurisés ou sous contraintes réglementaires fortes.

FAQ : sécuriser vos dépendances PHP et Drupal

Comment savoir si mon projet Drupal est actuellement vulnérable ?

Vérifiez si votre fichier de configuration contient des modules personnalisés sans dépôt privé associé configuré comme canonique. Si Composer doit chercher vos packages internes sur les registres publics par défaut, votre projet présente un risque.

Est-ce que Composer 2 est nativement protégé contre la confusion de dépendances ?

Composer 2 introduit des avertissements clairs et permet de configurer facilement des dépôts canoniques, mais il ne bloque pas par défaut l'installation d'un paquet public si aucune règle stricte de filtrage ou de canonicité n'a été rédigée dans votre configuration.

Quel est l'impact de l'option repo.packagist: false ?

Cette option désactive complètement le dépôt public Packagist. Cela signifie que vous devez héberger ou déclarer manuellement l'intégralité des dépendances de votre projet sur vos propres serveurs, ce qui élimine totalement le risque mais augmente la maintenance.

Pourquoi utiliser l'option --frozen-lockfile en production ?

Cette option force l'installation à échouer si le fichier de verrouillage n'est pas parfaitement à jour par rapport aux modifications récentes du fichier principal, évitant ainsi des installations de paquets non validés durant l'intégration continue.

Cyprien Prouvot

Cyprien Prouvot

Associé & Directeur Technique

Associé de l'agence, Cyprien pilote la vision technique et garantit la qualité des développements web. Il encadre les équipes internes et conseille les clients sur les choix d'architecture ou d'outils digitaux les plus pertinents. Il intervient également sur ce blog pour décrypter l'écosystème web, les tendances tech et les bonnes pratiques de conception.


Prêt ? Partez.

Que ce soit pour vous aider à faire le point sur vos besoins ou vous présenter les avantages et fonctionnalités de nos solutions, nous sommes là.
 

Back to top