5 façons d’empêcher l’injection de code PHP
DeveloperSteve Coochin
29 juin 2021
0 minutes de lectureDans la continuité de mon précédent article sur la recherche de vulnérabilités de sécurité dans PHP Composer, j’ai pensé que cet article pourrait vous aider à créer des applications plus sécurisées, qui empêchent l’injection de code PHP. En tant que développeurs, nous créons des applications pour simplifier la vie des utilisateurs finaux. Qu’il s’agisse d’une application de divertissement, de travail ou de réseau social, notre objectif final est de protéger les utilisateurs en intégrant la sécurité au code. Adopter des pratiques de programmation sécurisées contribue non seulement à protéger les utilisateurs finaux, mais permet aussi de gagner du temps et de réduire les coûts de développement en évitant les retouches.
Qu’est-ce que l’injection de code ?
L’injection de code est une attaque qui transmet une charge utile malveillante par le biais d’un vecteur d’attaque vulnérable. Son objectif est de compromettre l’intégrité de l’application visée. L’attaquant peut envoyer du code PHP exécutable ou du JavaScript, qui s’exécute soit dans l’environnement d’exécution de l’application, soit dans le navigateur de l’utilisateur final. Il est important de noter que PHP ne propose pas de mécanisme d’échappement dédié au JavaScript, ce qui peut également poser problème. La vulnérabilité est traitée dans l’environnement d’exécution PHP, où le code est exécuté côté serveur ou dans le navigateur d’un utilisateur qui ne se doute de rien.
Comment empêcher l’injection de code en PHP
La sécurité du développement commence dès l’écriture du code, avec l’adoption de conventions de programmation sécurisée. Soyez attentif aux données saisies dans les champs visibles par les utilisateurs et à la manière dont elles sont traitées. Nettoyez toutes les données entrantes et veillez à la façon dont elles sont stockées dans votre application.
En règle générale, l’exécution dynamique de code ne doit jamais être autorisée dans une application. Par exemple, évitez autant que possible les fonctions PHP natives telles que shell_exec() et exec(), qui s’exécutent au niveau du système d’exploitation.
Il est vivement recommandé d’utiliser systématiquement un outil d’analyse de sécurité open source comme Snyk Open Source, qui analyse votre gestionnaire de paquets afin de repérer les vulnérabilités potentielles liées aux bibliothèques et aux dépendances utilisées. Je consulte aussi chaque jour la Snyk Vulnerability Database de Composer pour découvrir les techniques utilisées. Les pages de rapports détaillées vous permettent de remonter jusqu’aux dépôts, au niveau du code, pour comprendre comment les vulnérabilités ont été détectées.
5 façons d’empêcher l’injection de code lors du développement d’applications PHP
Évitez d’utiliser
exec(),shell_exec(),system()oupassthru()Évitez d’utiliser
strip_tags()pour nettoyer les donnéesSérialisation du code
Utilisez un linter de sécurité PHP
Utilisez un outil SAST pour détecter les problèmes d’injection de code
1. Évitez d’utiliser exec(), shell_exec(), system() ou passthru()
Comme le dit l’expression : « Ici, il y a des dragons. » En règle générale, évitez autant que possible d’utiliser des fonctions PHP qui peuvent appeler directement l’environnement d’exécution. Du point de vue des vecteurs d’attaque, cela ouvre la porte à toutes sortes de problèmes dans votre pile technologique.
Traditionnellement, des fonctions comme exec(), shell_exec(), system(), et passthru() servaient à compresser ou décompresser des fichiers, à créer des tâches cron, voire à parcourir les fichiers et les dossiers du système d’exploitation. Aucune de ces fonctions n’effectue de nettoyage intégré du code, ce qui pose problème, en particulier lorsqu’elles sont utilisées avec des données utilisateur directes qui n’ont été ni validées ni nettoyées.
PHP propose des opérateurs fonctionnels intégrant l’échappement des données, notamment escapeshellcmd() et escapeshellarg(), qui appliquent un certain niveau d’échappement et de nettoyage aux données fournies lors de leur appel. Il existe plusieurs façons plus sûres d’obtenir les mêmes résultats.
Depuis PHP 7.4, l’archivage peut être effectué avec la classe ZipArchive, activée à l’aide de l’option --with-zip lors de la compilation de PHP. Il convient toutefois de rester prudent, car cette méthode peut tout de même permettre une attaque par traversée de répertoires.
Gérez les interactions avec le système de fichiers par programmation en limitant les moyens d’y accéder directement à partir des données fournies. PHP dispose de fonctions de gestion des fichiers qui évitent d’appeler directement le système d’exploitation. Par exemple, la fonction scandir() renvoie la liste des fichiers d’un répertoire donné, que vous pouvez ensuite ouvrir ou référencer directement dans le code. Veillez à ne pas accéder aux fichiers créés à partir de données exposées, et faites attention aux fonctions de gestion des permissions sur les fichiers, comme chmod() et chown(). Il est également important de ne pas faire confiance à l’en-tête de contenu, qui peut être falsifié, et de valider le type de fichier.
Créez des tâches cron dynamiquement à l’aide d’une bibliothèque Composer comme cron. En réalité, cette bibliothèque est exécutée par une tâche cron qui lance ensuite ses propres tâches cron dans le cadre de son exécution globale. Il faut toujours veiller à ce qui est exécuté, mais cette approche permet de créer des processus par programmation sans exposer l’accès au système d’exploitation.
2. Évitez d’utiliser strip_tags() pour nettoyer les données
Il faut toujours faire très attention au nettoyage et au traitement des données saisies par les utilisateurs. L’objectif est d’accepter les données valides, puis de les stocker et de les traiter correctement pour éviter que votre application ne devienne vulnérable. N’oubliez pas que les données saisies constituent un vecteur d’attaque ouvert que des acteurs malveillants peuvent exploiter pour interagir avec votre application. Prenez donc le temps de toujours traiter correctement les données entrantes.
La fonction strip_tags() sert principalement à supprimer les balises HTML et PHP des données fournies. Cela signifie que les éléments comme le JavaScript, le SQL et d’autres données potentiellement malveillantes seront considérés comme valides par cette fonction. htmlentities() est une autre option utilisée pour nettoyer les données. Elle permet également de définir des jeux de caractères UTF, mais gardez à l’esprit qu’elle ne nettoie pas complètement les données.
Vous pouvez également filtrer les données à l’aide d’une expression régulière, par exemple avec preg_replace('/[^a-zA-Z0-9]+/', '', $text). Pour les jeux de caractères UTF-8, cette fonction ne renvoie que les lettres et les chiffres présents dans les données. Soyez également prudent avec des fonctions comme mb_strtolower() lors du nettoyage des données utilisateur. Par le passé, nous avons constaté des vulnérabilités dans certaines fonctions de chaînes multioctets (mb_), comme CVE-2020-7065, une vulnérabilité d’écriture hors limites causée par l’encodage UTF-32LE. Cela peut entraîner un dépassement de tampon sur la pile et permettre l’exécution de code.
Préférez une fonction comme filter_var(), qui valide et nettoie les données en fonction des options de filtre définies. Par exemple, l’utilisation de FILTER_SANITIZE_STRING et de FILTER_FLAG_STRIP_HIGH dans la fonction filter_var(), avec les données utilisateur, supprimera toutes les balises HTML ainsi que les caractères dont la valeur ASCII est > 127.
Il existe également plusieurs bibliothèques Composer couramment utilisées pour nettoyer les données entrantes. Une bibliothèque comme HTML purifier offre un nettoyage conforme aux normes HTML et une liste d’autorisations assez flexible, que vous pouvez adapter aux types de données que vous souhaitez accepter dans votre application.
3. Évitez unserialize() en PHP
Cette section pourrait facilement faire l’objet d’un article à part entière. Le sujet fait débat depuis des années, y compris parmi les développeurs qui travaillent sur PHP. Le manuel PHP souligne d’ailleurs les dangers liés à l’utilisation de la fonction unserialize(). Il précise notamment qu’il ne faut « pas lui transmettre de données utilisateur non fiables », car cela peut entraîner le « chargement et l’exécution de code ».
Unserialize() sert à convertir une classe en chaîne de caractères, qui peut être stockée puis transmise à d’autres fonctions ou mise en cache pour une utilisation ultérieure. Cela paraît plutôt inoffensif jusqu’à ce qu’on comprenne comment le code C sous-jacent gère en mémoire les données de unserialize() : c’est là que surviennent la plupart des problèmes.
Il est vivement recommandé d’utiliser un format de données standard, comme JSON, avec json_decode() et json_encode(). Cette méthode permet de transmettre de manière sûre des données sérialisées vers et depuis l’utilisateur, après les avoir nettoyées.
4. Utilisez un linter de sécurité PHP
Disposer des bons outils dans votre workflow de développement vous aide à produire dès le départ un code sécurisé et plus fonctionnel. Les linters permettent de réduire les erreurs et les problèmes potentiels lors du développement d’applications PHP, et de limiter les vulnérabilités dans le code source.
Il est également vivement conseillé de désactiver par défaut l’affichage des erreurs dans les configurations de PHP.ini. La désactivation de error_reporting = E_ALL, ~E_NOTICE et ~E_WARNING empêchera l’affichage d’erreurs qui pourraient révéler des informations sur l’environnement et la configuration de votre application.
Le langage PHP intègre son propre linter, qui affiche des messages d’erreur très détaillés lors de la validation. Vous pouvez l’appeler depuis la CLI (ou dans le cadre d’un framework de test) en exécutant PHP -l, suivi du fichier à vérifier. Son inconvénient est qu’il ne peut vérifier qu’un seul fichier à la fois, même s’il est possible de l’intégrer à une boucle pour parcourir plusieurs fichiers en une seule exécution.
PHPlint est une solution robuste et plus connue pour vérifier rapidement plusieurs fichiers. PHPLint peut être utilisé depuis la CLI ou comme bibliothèque installée via Composer. Vous pouvez aussi l’intégrer facilement à une image Docker. PHPLint vérifie le code PHP 7 et PHP 8. Bien que ses résultats soient assez détaillés, il permet de détecter les problèmes en exécutant plusieurs processus de linting à la fois.
PHP-Parrallel-Lint est un autre linter populaire. Il prend en charge PHP 5.3 à PHP 8.0 et peut exécuter plusieurs processus de linting plus vite que vous ne pouvez taper echo "hello world". PHP-Parrallel-Lint fournit des résultats plus détaillés que les solutions mentionnées précédemment, mais ne prend pas encore en charge la CLI ni une solution Docker prête à l’emploi.
5. Utilisez un outil de test statique de la sécurité des applications pour détecter les problèmes d’injection de code
PHPStorm est l’un des IDE les plus utilisés pour le développement PHP et intègre des fonctionnalités très pratiques d’analyse statique du code. En complément des linters présentés plus haut, PHPStorm s’intègre à Xdebug, un outil utile pour le linting, le profilage et, comme son nom l’indique, le débogage du code afin de réduire les erreurs. Pour les workflows de programmation sécurisée, PHPStorm permet d’imposer des règles de programmation et des normes PSR spécifiques, ce qui est particulièrement utile pour les applications conformes à la norme PCI.
Pour renforcer la sécurité du code, Snyk propose un plug-in pour PHPStorm qui peut analyser le code et les bibliothèques qu’il utilise afin de repérer les problèmes de qualité et les vulnérabilités. Facile à installer et à configurer, il est disponible dans la section des plug-ins de l’application.

VSCode est un autre outil de développement de plus en plus adopté par les développeurs, et notamment ceux qui utilisent PHP. Comme PHPStorm, il intègre un linter et permet d’imposer les normes PSR.
Rechercher les vulnérabilités dans VSCode avec Snyk est aussi simple que d’ouvrir un nouveau terminal VSCode et d’utiliser la CLI de Snyk pour exécuter snyk test, ou snyk monitor pour recevoir des alertes en continu sur votre base de code. Pour en savoir plus et commencer à utiliser la CLI de Snyk avec PHP, consultez mon précédent article sur la recherche de vulnérabilités de sécurité dans PHP Composer avec Snyk.
Mise à jour : nous avons également récemment lancé la prise en charge de PHP dans Snyk Code. Pour en savoir plus et essayer cette fonctionnalité, consultez l’article La prise en charge de PHP par Snyk Code pour l’analyse des vulnérabilités passe en version bêta.
En résumé
En tant que développeurs, nous créons des applications pour faciliter le quotidien des utilisateurs finaux. Veiller à leur sécurité permet non seulement de garantir la protection des données des utilisateurs, mais aussi de gagner leur confiance.
Détecter les vulnérabilités d’injection de code dès l’écriture du code lors du développement d’applications est facile, à condition de disposer dès le départ des bons outils et du bon workflow.
Lancez-vous dans les compétitions Capture The Flag
Apprenez à résoudre des défis Capture The Flag en regardant à la demande notre atelier virtuel d’initiation.



