PRÉVENTION DES ATTAQUES PAR INJECTION SQL DANS PHP | EXEMPLES DE CODE

Après plusieurs années de tests de pénétration et de travail avec les équipes de développement pour sécuriser leurs systèmes, j’ai remarqué qu’il n’y avait pas d’article de blog complet qui donne une explication détaillée de la façon de prévenir les attaques par injection SQL dans tous les langages et frameworks de développement d’applications Web les plus populaires. Par conséquent, j’ai décidé d’écrire une série de plusieurs articles pour en faire le guide le plus complet pour empêcher l’injection SQL.

Ce guide est une série ouverte de messages et c’est le premier message. Si vous voulez que j’ajoute une technologie, veuillez simplement commenter ci-dessous et je serai très heureux d’expliquer la prévention de l’injection SQL pour cette technologie. Ce guide analysera les technologies suivantes:

  • PHP
    • Laravel
    • Symfony
    • CodeIgniter
    • CakePHP
    • L-XL
    • Zend
  • JEE
    • JDBC
    • JPA
  • ASP.NET
    • ADO.NET
    • ADO.Dopper NET
    • NHibernate

Ce premier article se concentrera sur le langage PHP et tous ses frameworks. D’autres articles suivront pour traiter des autres technologies énumérées.

Pour ceux qui ne savent pas ce qu’est une attaque par injection SQL, laissez-moi faire une petite introduction pour leur expliquer cela. Les attaques par injection SQL se produisent lorsqu’un mauvais utilisateur tente d’injecter une requête SQL malveillante dans une requête légitime. L’impact de l’attaque par injection SQL diffère d’une situation à l’autre en fonction de plusieurs éléments liés à l’environnement de l’application, et cela peut aller d’une situation aussi “simple” qu’une fuite d’informations à un contrôle complet du serveur. Je n’irai pas plus loin dans la façon dont un attaquant pourrait exploiter cette vulnérabilité ou comment nous pouvons la découvrir dans les tests blackbox car ce n’est pas l’objectif de ce post. Ce que nous verrons, c’est comment le découvrir dans le code source et comment le réparer.

Comment empêcher une attaque par injection SQL dans un code source PHP pur ?

Commençons notre voyage avec l’une des technologies de développement PHP les plus anciennes et les plus populaires sans aucun framework. Maintenant, la première chose à faire est de savoir à quoi ressemble une ligne de code source vulnérable pour pouvoir identifier la vulnérabilité.

Voici un exemple de requête d’authentification:

$request = "SELECT * FROM users WHERE username=". $_POST ." AND password = ". $_POST;
$result = $mysqli->query($request);

Maintenant, si vous regardez de près la requête, vous remarquerez que les paramètres utilisateur $_POST et $_POST sont directement injectés dans la requête SQL sans aucun filtrage. Cela rend cette ligne de code vulnérable à l’injection SQL.

Pour corriger cette vulnérabilité, vous devrez filtrer les données avant de les utiliser dans la requête sql. Pour ce faire, la meilleure solution consiste à utiliser les instructions préparées. Voici un exemple de la façon dont vous pouvez utiliser les instructions préparées pour corriger cette vulnérabilité:

$conn = new mysqli($servername, $username, $password, $dbname);
$stmt = $conn->prepare("SELECT * FROM users WHERE username=? AND password=?");
$stmt->bind_param($username, $password);
$stmt->execute();

Bonne pratique

Vous avez la possibilité de ne pas utiliser la fonction bind_param() voici un exemple de la façon de procéder :

$conn = new mysqli($servername, $username, $password, $dbname);
$stmt = $conn->prepare("SELECT * FROM users WHERE username=:username AND password=:password");
$stmt-> execute(array('username'=>$_POST,'password'=>$_POST));

Après des années d’analyse des codes sources I j’ai découvert une erreur intéressante que de nombreux développeurs font en corrigeant cette vulnérabilité. Jetez un coup d’œil à l’exemple suivant:

$stmt = $conn->prepare("SELECT * FROM users WHERE username=".$_POST." AND password=".$_POST);

Si vous regardez cet exemple, vous verrez que nous utilisons les déclarations préparées. Cependant, les paramètres sont directement injectés dans la requête SQL. C’est une erreur très courante que font les développeurs et qui rend le code vulnérable à l’injection SQL même si nous utilisons les instructions préparées.

L’idée derrière les instructions préparées est de filtrer les paramètres avant de les injecter dans la requête SQL … gardez cela à l’esprit.

Note très importante :

PHP propose d’autres fonctions pour filtrer les requêtes SQL et empêcher l’injection SQL, mais certaines d’entre elles ne sont pas efficaces. La fonction mysql_real_escape_string(), par exemple, est l’une des fonctions PHP les plus connues pour empêcher l’injection SQL. Malheureusement, cette fonction est vraiment efficace. En utilisant cette fonction, la bibliothèque MySQL ajoutera une barre oblique inverse aux caractères suivants: NULL, \x00, \n, \r, \, ‘, ” et \x1a. Cependant, pour une situation comme celle-ci :

$id = mysql_real_escape_string("1 OR 1=1");
$request = "SELECT * FROM users WHERE id = $id";

En rouge, c’est ce qu’un mauvais utilisateur vous enverra

La chaîne mysql_real_escape_string() ne vous protégera pas contre une telle attaque.

C’est pourquoi je recommande toujours d’utiliser les instructions préparées au lieu de cette fonction.

Comment prévenir les attaques par injection SQL dans Laravel framework?

Maintenant que nous avons vu l’attaque par injection SQL contre un code PHP pur, il est maintenant temps de voir comment nous pouvons corriger la vulnérabilité dans une application Web basée sur un framework. Avant de commencer à en parler, permettez-moi d’abord de vous donner une petite description du framework Laravel.

Laravel est un framework web open-source écrit en PHP respectant le principe model-view-controller et entièrement développé en programmation orientée objet.

Exemple 1

Voici un exemple de requête SQL vulnérable dans un laravel :

$user = DB::select('select * from users where username='. $request->post('username').' AND password='. $request->post('password'));

Le framework Laravel offre des fonctions impressionnantes pour aider les développeurs à sécuriser la requête SQL contre les injections malveillantes. Malheureusement, la plupart des vulnérabilités d’injection SQL que je découvre dans de telles applications sont liées à une mauvaise utilisation des fonctions Laravel.

Analysons l’exemple précédent, selon la documentation de Laravel, la fonction DB::select pourrait recevoir deux paramètres. Le premier est la requête SQL et le second les paramètres. Donc, pour filtrer les paramètres de l’injection SQL, vous devrez insérer les paramètres dans la requête en utilisant le deuxième paramètre comme dans cet exemple :

useruser=DB::select(‘select* from users where username=? ET mot de passe =?’, );

Exemple 2

Laravel a plusieurs façons de communiquer avec la base de données dans cet exemple, vous verrez que laravel force le développeur à utiliser certaines fonctions qui filtrent automatiquement les données utilisateur, comme l’exemple suivant:

Users::where('username', $request->get('username'))->orderBy($request->get('orderby'))->get();

Cette fonction est censée être sécurisée contre l’injection SQL et ils le sont, le seul problème est au niveau de la fonction orderBy(). Selon la documentation de Laravel, cette fonction ne filtre pas les données utilisateur, de sorte qu’une attaque par injection SQL est toujours possible via cette fonction.

La meilleure chose à faire n’est pas de donner à l’utilisateur la possibilité de contrôler le nom de la table, mais si cela est inévitable, vous devrez utiliser une liste blanche pour valider les données utilisateur avant de les insérer dans cette fonction.

Comment empêcher une attaque par injection SQL dans Symfony ?

Voyons un autre framework PHP bien connu qui offre un ensemble de composants PHP réutilisables pour accélérer le développement d’applications PHP. Symfony est également utilisé par certains des CMS web les plus connus comme Drupal et Magento.

Symfony est l’un des frameworks les plus sécurisés avec lesquels vous pouvez travailler, il offre un grand nombre de fonctions pour écrire un code sécurisé. Cependant, si ces fonctions ne sont pas bien utilisées, vous obtenez un code vulnérable. Voici donc un exemple de code vulnérable:

$entityManager = $this->getEntityManager();
$query = $entityManager->createQuery('SELECT p
FROM App\Entity\Product p
WHERE p.price > '. $request->query->get('price')
);

Analysons maintenant ce code vulnérable. Selon la documentation de Symfony, createQuery() est une fonction qui utilise par défaut les instructions préparées. Ainsi, l’objet résultant d’une telle fonction vous donne accès à la fonction setParameter(). Celui-ci filtre toutes les données utilisateur pour éviter une injection SQL. Voici un exemple de comment l’utiliser :

$entityManager = $this->getEntityManager();
$query = $entityManager->createQuery('SELECT p
FROM App\Entity\Product p
WHERE p.price > :price
ORDER BY p.price ASC'
)->setParameter('price', $price);

Comment prévenir les attaques par injection SQL dans Codeigniter ?

CodeIgniter l’un des frameworks PHP les plus puissants, légers et populaires avec un très faible encombrement. Il a été conçu pour les développeurs qui ont besoin d’une boîte à outils simple et élégante pour créer des applications Web complètes. Comme Symfony et Laravel, Codeigniter est également livré avec certains systèmes de sécurité pour aider les développeurs à créer des applications plus sûres.

Cependant, également avec Codeigniter, certains développeurs font la même erreur et injectent les données utilisateur sans aucun filtrage. Voici un exemple de telles erreurs qui conduisent à une attaque par injection SQL:

$query = 'SELECT * FROM users WHERE username = '. $this-> input->post('username'). ' AND password= '. $this-> input->post('password');
$this->db->query($query);

Maintenant, pour résoudre ce problème, vous devrez jeter un œil à la documentation de Codeigniter. Selon elle, la fonction query() prend deux paramètres. Le premier est la requête sql et le second les paramètres que vous souhaitez lier.

Exemple 1

Par défaut, cette fonction filtre tous les paramètres liés pour éviter les injections SQL. Voici un exemple de la bonne façon d’utiliser cette fonction:

$query = 'SELECT * FROM users WHERE username = ? AND password = ? ';
$this->db->query($query, array($this->input->post('username'), $this-> input-> post('password')));

Exemple 2:

Codeigniter fournit un autre moyen d’effectuer une requête SQL pour empêcher les injections SQL. Voici un exemple d’utilisation de la classe Active Record pour empêcher les injections SQL:

$this->db->get_where('users',array('username'=>$this->input->post('username') ,'password' => $this->input->post('password')));

La fonction get_where() n’existe plus dans la version 4 de Codeigniter.

Remarque 1

Codeigniter offre également une fonction qui ressemble à la fonction mysql_real_escape_string() appelée escape(). Mais, comme je l’ai dit pour la première fois, mysql_real_escape_string() pourrait être contourné dans certains cas et vous devez éviter de l’utiliser. Le fait que la fonction escape() fasse exactement la même chose, alors il serait possible de la contourner également. C’est pourquoi je n’encourage pas les développeurs à utiliser la fonction escape().

Remarque 2

Comment prévenir les attaques par injection SQL dans CakePHP ?

CakePHP est un framework de développement rapide pour PHP qui utilise des modèles de conception communément connus comme le Mappage de données Associatives, le Contrôleur frontal et le MVC. CakePHP propose un tas de composants intégrés pour faciliter le développement d’applications web et les rendre plus sécurisées.

Cependant, les applications CakePHP sont également vulnérables aux injections SQL si elles ne sont pas bien utilisées. Voici un exemple de code vulnérable à une attaque par injection SQL dans ce cadre:

$results = $connection-> execute('SELECT * FROM users WHERE username = '. $this->request->getParam('username').' AND password='. $this->request->getParam('password'))->fetchAll('assoc');

La fonction execute() utilise par défaut les instructions préparées. Cependant, l’exemple précédent reste vulnérable aux attaques par injection SQL, car les données utilisateur sont insérées directement dans une requête sql légitime. Voici un exemple de la bonne façon d’utiliser cette fonction:

$results = $connection->execute('SELECT * FROM users WHERE username = :username AND password=:password', )->fetchAll('assoc');

Le framework CakePHP offre également un système appelé Query Builder, qui force le filtrage des données utilisateur pour empêcher les attaques par injection SQL. Selon la documentation de CakePHP, Sous les couvertures, le générateur de requêtes utilise les instructions préparées.

Voici un exemple de la façon dont vous pouvez utiliser un tel système de la bonne manière:

use Cake\ORM\Locator\LocatorAwareTrait;
$users = $this->getTableLocator()->get('users');
// Start a new query.
$query = $users->find();
$query->where();

Comment prévenir les attaques par injection SQL dans FuelPHP ?

FuelPHP est l’un des framework PHP les plus récents qui est né sur la base des meilleures idées de chaque framework sur le marché. Il a été développé avec PHP 5 et est entièrement orienté objet. Dans FuelPHP, la sécurité était au centre des préoccupations, ce qui a poussé ses contributeurs à mettre en œuvre de nombreux mécanismes de sécurité pour filtrer les données des utilisateurs. L’injection SQL attaque l’une des raisons d’implémenter de tels mécanismes dans FuelPHP.

Cependant, même avec un framework aussi puissant, une simple utilisation abusive de ces mécanismes met tout le code en danger pour une attaque par injection SQL. Voici un exemple d’une telle erreur de code:

$query = "SELECT * FROM article WHERE id = ". Input::get('id');
$result = DB::query($query)->execute();

Pour résoudre ce problème, il existe également deux techniques comme les autres frameworks. La première consiste à utiliser correctement la fonction query(), en liant les paramètres comme dans l’exemple suivant :

$query = "SELECT * FROM article WHERE id = :id"; // our query
$result = DB::query($query)->bind('id', Input::get('id'))->execute();

La deuxième solution consiste à utiliser le mécanisme ORM implémenté dans le framework FuelPHP pour communiquer avec la base de données. Voici un exemple de comment l’utiliser:

$user = DB::select()->from('article')->where('id', Input::get('id'))->execute();

Comment prévenir les attaques par injection SQL dans zend framework ?

Zend framework (changé en Laminas Project) est l’un des framework les plus connus dans le monde de PHP. Il a été développé avec un réglage des performances à l’esprit, ce qui explique pourquoi chaque nouvelle version est beaucoup plus rapide que l’ancienne. Zend a également été développé avec les meilleures pratiques de sécurité, ce qui a poussé ses développeurs à mettre en œuvre des mécanismes de sécurité supplémentaires pour faire face aux cybermenaces connues.

Certains de ces mécanismes ont été implémentés pour faire face aux attaques par injection SQL. Mais comme toujours, une mauvaise utilisation de ces mécanismes conduit à un code vulnérable. Dans cette partie de l’article, je vais montrer un exemple d’une telle erreur:

$adapter->query('SELECT * FROM `article` WHERE `id` = '. $this->getRequest()->getPost('id'));

Voici comment corriger cette vulnérabilité:

$adapter->query('SELECT * FROM `article` WHERE `id` = ?', );

Leave a Reply