Introduction
Tous les quelques mois, la même histoire ressort dans l'écosystème Magento : une boutique en ligne est compromise, et ce n'est presque jamais le cœur du CMS qui est en cause. C'est une extension tierce — souvent installée pour de bonnes raisons, comme accélérer le chargement des pages ou automatiser un import de produits. Derrière la majorité de ces incidents se cache une vulnérabilité que les développeurs PHP traînent depuis quinze ans sans toujours la voir : l'injection d'objet via unserialize().
Cet article s'appuie sur l'analyse technique de Robin Dhiman, publiée sur dev.to sous le titre "unserialize() is the Magento footgun nobody audits". Nous reprenons ici le mécanisme en détail, en insistant sur un point souvent négligé : ce sont les extensions tierces, et non le code core, qui ouvrent la voie aux attaquants.
Comment une simple chaîne de caractères devient une porte dérobée
La fonction unserialize() ne se contente pas de reconstruire des tableaux ou des chaînes de texte à partir d'une représentation sérialisée. Elle peut aussi recréer des objets PHP complets — et instancier n'importe quelle classe accessible dans le code appelant, en remplissant ses propriétés directement depuis la chaîne fournie en entrée.
Le problème devient critique dès qu'un attaquant contrôle cette chaîne. À ce moment-là, il contrôle :
- quels objets sont créés,
- avec quelles propriétés ils sont initialisés.
Un objet isolé, en soi, n'est pas dangereux. Le vrai risque vient des méthodes magiques de PHP. Lorsqu'un objet est détruit, __destruct() s'exécute automatiquement. Lorsqu'il est réveillé après désérialisation, __wakeup() s'exécute. Dans une base de code aussi vaste que Magento — le core, le framework, et une trentaine d'extensions avec leurs propres dépendances — il existe presque toujours une classe dont le __destruct() écrit un fichier sur le disque, dont le __wakeup() ouvre une connexion réseau, ou qui peut être chaînée avec d'autres classes pour produire un effet similaire.
Ce mécanisme d'enchaînement de classes a un nom dans la communauté sécurité : les gadget chains. L'attaquant n'a pas besoin que votre code soit mal écrit en apparence. Il a simplement besoin que votre application soit volumineuse, et qu'un point d'entrée appelle unserialize() sur une donnée qu'il peut influencer, même indirectement.
Pourquoi les extensions tierces sont le maillon faible
C'est précisément là que le risque explose dans l'écosystème Magento. Le core de Magento est audité, testé, suivi par une équipe de sécurité dédiée. Les extensions tierces, elles, vivent dans une zone grise :
- elles sont souvent développées par de petites équipes sans processus de revue de sécurité formalisé,
- elles sont installées pour des gains rapides — accélérer le cache, automatiser un import CSV, synchroniser un ERP — sans audit préalable du code,
- elles ajoutent des dizaines de classes supplémentaires au projet, augmentant mécaniquement la surface disponible pour construire une gadget chain,
- elles sont rarement mises à jour aussi rigoureusement que le core, et restent parfois actives des années après avoir été désactivées en apparence.
Une extension "gratuite" ou peu coûteuse qui promet un gain de performance immédiat est séduisante. Mais chaque classe PHP qu'elle ajoute au projet est une pièce potentielle du puzzle qu'un attaquant peut assembler. Le risque n'est pas que cette extension soit malveillante — c'est qu'elle soit simplement présente, avec une méthode magique mal protégée, dans une base de code où une autre faille (souvent bien plus anodine, comme un paramètre non filtré dans un formulaire) permet d'injecter une chaîne sérialisée.
C'est l'angle mort classique des audits de sécurité superficiels : on regarde le code qu'on a écrit soi-même, on fait confiance par défaut au code qu'on a téléchargé. Or c'est précisément cette confiance qui transforme une extension de cache en porte d'entrée vers la base de données client.
Comment limiter le risque concrètement
Quelques principes simples permettent de réduire drastiquement cette exposition :
Ne jamais appeler unserialize() sur une entrée non fiable. Depuis PHP 7, l'option allowed_classes permet de restreindre quelles classes peuvent être instanciées :
// Dangereux : autorise n'importe quelle classe
$data = unserialize($input);
// Plus sûr : interdit toute instanciation d'objet
$data = unserialize($input, ['allowed_classes' => false]);
Privilégier json_encode() / json_decode() pour tout échange de données structurées avec l'extérieur. JSON ne permet pas d'instancier des objets arbitraires, ce qui élimine la classe entière de vulnérabilité.
Auditer systématiquement les extensions tierces avant installation, et pas seulement au moment de l'achat. Un grep rapide sur unserialize( dans le code source d'un module, croisé avec l'origine des données passées en paramètre, suffit souvent à repérer un point d'entrée suspect.
Maintenir un inventaire des extensions actives et désactivées. Une extension désinstallée "à moitié" — fichiers toujours présents, mais module désactivé dans la configuration — peut rester exploitable si ses classes sont encore chargées ailleurs dans l'application.
Mettre en place une revue de code régulière, en particulier sur les modules touchant à l'import de données, à la mise en cache ou à la communication avec des services externes : ce sont les points de contact privilégiés avec des données potentiellement contrôlées par un attaquant.
Conclusion
La leçon de cette analyse n'est pas que Magento est intrinsèquement fragile, mais que la taille d'un projet est, en elle-même, une surface d'attaque. Chaque extension ajoutée — même la plus anodine en apparence — augmente le nombre de classes disponibles pour construire une gadget chain. Le coût d'une extension ne se mesure pas seulement en euros ou en heures de configuration : il se mesure aussi en risque ajouté à la base de code.
Chez MulerTech, nous recommandons systématiquement un audit du code source avant toute installation d'extension tierce sur un projet Symfony ou Magento, avec une attention particulière portée aux appels à unserialize() et aux méthodes magiques __wakeup() et __destruct(). C'est un réflexe simple, qui prend quelques minutes en revue de code, et qui évite des incidents de sécurité dont le coût se chiffre en jours d'indisponibilité et en confiance client perdue.
Article inspiré de l'analyse originale de Robin Dhiman, "unserialize() is the Magento footgun nobody audits", publiée sur dev.to.