Injections PHP - Au-delà du SQL - Sécurisez votre API

Étienne Lambert .

2 avril 2026

Code binaire avec le terme "SQL INJECTION" en surbrillance, signalant une vulnérabilité potentielle.

Les injections côté PHP ne se résument pas au SQL. Le vrai risque apparaît dès qu’une donnée contrôlée par l’utilisateur traverse une frontière de confiance et devient du code, un chemin de fichier, une requête dynamique ou un objet désérialisé. Dans un backend ou une API, c’est souvent cette bascule qui transforme une simple entrée en exécution côté serveur, et c’est précisément ce que je vais clarifier ici.

Les points à surveiller avant tout sont la frontière entre données et exécution côté serveur

  • Une faille d’injection PHP peut prendre plusieurs formes, pas seulement l’injection SQL.
  • Les entrées les plus sensibles sont celles qui alimentent `eval`, `include`, `require`, `unserialize` ou des requêtes construites à la main.
  • Les requêtes préparées protègent le SQL, mais elles ne couvrent pas tous les autres sinks dangereux.
  • Les listes blanches, la validation de schéma et le moindre privilège réduisent fortement la surface d’attaque.
  • Dans une API, l’audit utile commence par les points d’entrée externes, puis remonte vers les traitements sensibles.

Ce que recouvre réellement une injection en PHP

Je préfère parler d’injection de code ou de données interprétées comme du code, parce que le problème est plus large qu’un simple mot-clé. L’OWASP Top 10 2025 garde d’ailleurs l’injection parmi les risques centraux des applications web, justement parce que les variantes sont nombreuses et qu’elles visent toutes le même défaut de conception : laisser une entrée non fiable piloter un interpréteur.

En pratique, une application PHP devient fragile dès qu’elle confond données et instructions. Cela peut prendre plusieurs formes :

  • injection SQL, quand une requête est assemblée par concaténation ;
  • injection de code, quand `eval()` ou un mécanisme équivalent exécute du texte fourni indirectement par l’utilisateur ;
  • inclusion de fichiers, quand un paramètre influence `include` ou `require` sans contrôle strict ;
  • désérialisation dangereuse, quand `unserialize()` traite des données externes ;
  • injection de commande système, quand un argument remonte jusqu’à `exec`, `system` ou un équivalent shell.

La documentation officielle de PHP insiste sur ce point depuis longtemps : on doit traiter les données utilisateur comme des données, pas comme des morceaux de syntaxe. Une fois ce cadre posé, on voit beaucoup plus clairement où le backend bascule vers un comportement dangereux, et c’est là qu’il faut regarder les entrées les plus exposées.

Exemples de code PHP illustrant des vulnérabilités de php injection, utilisant `shell_exec`, `passthru` et des backticks pour exécuter des commandes système.

Les points d’entrée les plus exposés dans une API

Dans les projets backend, je retrouve presque toujours les mêmes zones de fragilité. Le problème n’est pas le format de l’entrée, mais ce que le code en fait ensuite. Un simple paramètre JSON peut devenir une requête, un nom de fichier, un nom de classe ou un fragment de logique métier si la chaîne de traitement n’est pas verrouillée.

Point d’entrée Ce que le code fait souvent Risque réel
Paramètre de requête ou corps JSON Construit une requête SQL ou une condition dynamique Injection SQL ou logique métier manipulée
Paramètre de route Sert à choisir un fichier, un template ou une ressource Inclusion de fichier, lecture non autorisée, exécution indirecte
Champ de tri ou de filtre Alimente `ORDER BY`, `WHERE`, ou un builder artisanal Injection de clause, contournement de filtre, exposition de données
Donnée sérialisée Est passée à `unserialize()` ou à un format trop permissif Objet forgé, déclenchement de méthodes magiques, gadget chain
Nom de classe, de méthode ou de callback Est résolu dynamiquement Appel imprévu, exécution d’un chemin non prévu par le développeur
Argument de commande ou de script Va jusqu’à un appel système Commande injectée, fuite de données, compromission du serveur
En-tête HTTP ou champ d’upload Est réutilisé sans normalisation Nom de fichier piégé, parser abuse, comportement inattendu

Dans une API REST ou GraphQL, je suis particulièrement attentif aux champs de recherche, de tri, de pagination et aux paramètres qui pilotent le rendu. Ce sont des entrées anodines en apparence, mais elles passent souvent plus loin dans la pile applicative que les équipes ne l’imaginent. C’est justement ce chemin interne qu’il faut comprendre pour bloquer la faille au bon endroit.

Comment une faille devient une exécution de code

Une injection ne naît pas au moment où l’entrée arrive. Elle se construit en plusieurs étapes, et c’est pour ça que les revues de code rapides ratent souvent le problème.

  1. L’entrée externe arrive par HTTP, fichier, file upload ou message d’une autre API.
  2. Le backend la transforme, parfois avec une validation trop superficielle ou uniquement syntaxique.
  3. La valeur atteint un sink, c’est-à-dire un point d’exécution sensible comme une requête, un include, un appel système ou un décodeur d’objets.
  4. L’interpréteur traite alors la donnée comme une instruction, un nom ou une structure active au lieu de la considérer comme un simple texte.

Le défaut classique, c’est de croire qu’une validation amont suffit. En réalité, une entrée peut être valide pour la couche HTTP et dangereuse pour la couche SQL, puis encore différente pour la couche système. La défense doit donc suivre le contexte réel d’usage, pas seulement la forme de la donnée.

C’est pour cette raison qu’une bonne analyse commence toujours par identifier les sinks, puis par remonter vers les sources. Une fois cette cartographie faite, les protections efficaces deviennent beaucoup plus évidentes à appliquer.

Les protections qui tiennent vraiment la route

Je vois encore trop de projets compter sur une seule mesure, alors qu’en sécurité applicative les contrôles doivent se compléter. Une protection utile contre l’injection PHP n’est pas une astuce magique : c’est un ensemble de garde-fous qui ferment les chemins d’exécution un par un.

Mesure Ce qu’elle bloque Sa limite
Requêtes préparées avec `PDO::prepare()` ou `mysqli::prepare()` L’injection SQL classique par concaténation Ne protège pas les noms de table, de colonne ou les autres interpréteurs
Listes blanches explicites Les valeurs inattendues pour un fichier, une classe, un tri ou un template Demande une cartographie claire des cas autorisés
Validation de schéma sur l’entrée JSON Les structures malformées, les types incohérents et les champs parasites Ne remplace pas le contrôle métier ni la sécurité au sink
Suppression des fonctions dangereuses `eval`, les appels shell, `unserialize` sur données externes, les includes dynamiques Peut demander une refonte partielle du code legacy
Moindre privilège côté base et système de fichiers La portée d’un compte compromis N’empêche pas l’exploitation initiale si le code reste vulnérable
Encodage de sortie L’exécution côté navigateur Utile pour XSS, pas pour les injections serveur
Journalisation et alertes Les tentatives répétées et les payloads anormaux Détecte tardivement, ne corrige pas la faille

Si je devais résumer la logique défensive en une phrase, je dirais ceci : paramétrez quand le moteur attend des données, listez quand le code attend un choix, et supprimez tout ce qui transforme l’entrée en langage interprété. Cette approche colle bien à PHP et à ses usages backend, parce qu’elle réduit le nombre de contextes où une entrée peut changer de nature.

La documentation PHP va dans le même sens pour les accès base de données : on ne colle pas les valeurs utilisateur dans la requête, on les lie comme paramètres. Ce n’est pas un détail de syntaxe, c’est la différence entre une donnée traitée comme donnée et une donnée traitée comme code SQL. Une fois cette base solide posée, il reste à éviter les erreurs de mise en œuvre qui font revenir le problème par la porte de service.

Les erreurs que je vois le plus souvent en audit

Les failles sérieuses ne viennent pas toujours d’un oubli spectaculaire. Elles viennent souvent d’un raccourci raisonnable en apparence, puis répété partout dans le code.

  • Confondre validation et sécurité complète, alors que la validation ne garantit pas le bon contexte d’exécution.
  • Utiliser `addslashes()` ou un filtre maison comme si cela remplaçait les requêtes préparées.
  • Laisser un paramètre `sort`, `path`, `template` ou `driver` atteindre directement un sink sensible.
  • Employer `unserialize()` sur des données externes parce que c’est rapide à brancher.
  • Valider seulement côté front, puis supposer que l’API recevra toujours des données propres.
  • Considérer un ORM comme un bouclier automatique alors que les requêtes brutes ou les fragments dynamiques restent dangereux.
  • Masquer les erreurs en production sans conserver de logs exploitables pour l’analyse.

Je me méfie aussi des systèmes qui “fonctionnent” tant que les paramètres restent parfaits. C’est souvent là que la faille se cache : le code n’a pas été écrit pour refuser proprement l’inattendu. Et c’est exactement ce qu’il faut corriger quand on reprend un backend existant.

Ce que je corrige en premier sur un projet legacy

Sur un code base ancien, je ne cherche pas à tout réécrire d’un coup. Je commence par les zones qui sont exposées publiquement et qui peuvent déclencher une exécution, une requête ou un accès fichier. C’est là que le retour sur effort est le meilleur.

  1. Je dresse la liste des sinks dangereux : `eval`, `include`, `require`, `unserialize`, `exec`, `system`, requêtes concaténées et callbacks dynamiques.
  2. Je remplace les choix libres par des listes blanches ou par des mappings explicites.
  3. Je convertis les accès base en requêtes préparées, même si cela demande de toucher plusieurs couches.
  4. Je verrouille les permissions du compte applicatif, de la base et du système de fichiers.
  5. J’ajoute des tests de non-régression avec des entrées malformées, inattendues et hors liste.
  6. J’automatise un contrôle de sécurité dans la CI pour éviter que le problème revienne plus tard.

Cette séquence est pragmatique : elle ne suppose pas une base de code parfaite, elle réduit d’abord le risque le plus concret. Une fois ces corrections en place, on peut durcir le reste du backend avec beaucoup plus de sérénité.

Le réflexe le plus rentable pour garder un backend PHP sain

Si je ne devais garder qu’une règle, ce serait celle-ci : une entrée externe ne doit jamais devenir du code sans passer par un mécanisme strictement borné. Dès qu’un projet commence à assembler des instructions, des chemins ou des requêtes avec des morceaux contrôlés par l’utilisateur, le niveau de vigilance doit remonter immédiatement.

Dans un backend ou une API PHP moderne, la sécurité tient rarement à une seule fonction. Elle repose plutôt sur une discipline simple mais constante : réduire les points d’entrée, supprimer les interprétations dangereuses, préférer les paramètres aux concaténations et vérifier que chaque donnée reste une donnée jusqu’au bout.

C’est cette logique qui fait la différence entre une application qui résiste bien aux abus courants et une application qui laisse trop de place à l’improvisation côté attaquant.

Questions fréquentes

C'est quand des données utilisateur non fiables sont interprétées comme du code, un chemin de fichier, une commande système ou un objet désérialisé, menant à une exécution côté serveur non intentionnelle.
Les paramètres de requête, les corps JSON, les paramètres de route, les champs de tri/filtre, les données sérialisées et les en-têtes HTTP sont souvent des vecteurs d'attaque si mal gérés.
Elles séparent les données des instructions SQL, empêchant l'interprétation des entrées utilisateur comme du code SQL. Cependant, elles ne protègent pas contre d'autres types d'injections (code, fichier, etc.).
Utilisez des listes blanches, la validation de schéma, supprimez les fonctions dangereuses, appliquez le moindre privilège et encodez les sorties. Ne confondez jamais validation et sécurité complète.
Une entrée peut être valide pour une couche (ex: HTTP) mais dangereuse pour une autre (ex: SQL, système de fichiers). La défense doit s'adapter au contexte réel d'utilisation de la donnée.

Évaluer l'article

Moyenne: 0.0 / 5 · 0 évaluations

Tags

php injection sécurité injection php backend prévention injection php api failles injection php
Autor Étienne Lambert
Étienne Lambert
Je m'appelle Étienne Lambert et j'ai 13 ans d'expérience dans le développement web, avec un accent particulier sur JavaScript, le backend, NoSQL et la sécurité. Mon parcours dans ce domaine a commencé par une curiosité insatiable pour la technologie et la manière dont elle façonne notre monde. J'aime partager mes connaissances et aider les lecteurs à naviguer dans les complexités du développement web, en rendant des sujets parfois ardus plus accessibles. Je m'efforce toujours de fournir des informations utiles, précises et à jour, en vérifiant mes sources et en comparant les différentes perspectives. J'écris sur des sujets variés qui vont des meilleures pratiques en matière de sécurité aux tendances émergentes dans le développement. Mon objectif est de simplifier des concepts techniques et d'organiser les connaissances de manière claire, afin que chacun puisse en tirer profit et se sentir confiant dans ses compétences en développement web.

Commentaires (0)

Ajouter un commentaire