Geekflare est soutenu par son public. Nous pouvons percevoir des commissions d'affiliation sur les liens d'achat présents sur ce site.
En Développement et Sécurité Dernière mise à jour : 25 septembre 2023
Partager sur :
Invicti Web Application Security Scanner - la seule solution qui offre une vérification automatique des vulnérabilités avec Proof-Based Scanning™.

Vous pensez que votre base de données SQL est performante et à l’abri d’une destruction instantanée ? Eh bien, l’injection SQL n’est pas de cet avis !

Oui, c’est bien de destruction instantanée dont nous parlons, car je ne veux pas ouvrir cet article avec la terminologie habituelle et boiteuse de “renforcement de la sécurité” et de “prévention des accès malveillants” L’injection SQL est un si vieux truc que tout le monde, tous les développeurs, le connaissent très bien et savent comment l’éviter. À l’exception d’une fois où ils commettent une erreur, dont les résultats peuvent être désastreux

Si vous savez déjà ce qu’est l’injection SQL, n’hésitez pas à passer à la dernière partie de l’article. Mais pour ceux qui débutent dans le domaine du développement web et qui rêvent d’occuper des postes plus importants, une introduction s’impose

Qu’est-ce que l’injection SQL ?

La clé de la compréhension de l’injection SQL se trouve dans son nom : l’injection SQL. Le mot “injection” n’a ici aucune connotation médicale, mais correspond plutôt à l’utilisation du verbe “injecter” Ensemble, ces deux mots expriment l’idée d’introduire du code SQL dans une application web

Mettre du SQL dans une application web … hmmm … . N’est-ce pas ce que nous faisons de toute façon ? Oui, mais nous ne voulons pas qu’un attaquant pilote notre base de données. Comprenons cela à l’aide d’un exemple

Supposons que vous construisiez un site web PHP typique pour un magasin de commerce électronique local, et que vous décidiez d’ajouter un formulaire de contact comme celui-ci

<form action="record_message.php" method="POST">
 <label>Votre nom</label>
 <input type="text" name="name">
  
 <label>Votre message</label>
 <textarea name="message" rows="5"></textarea>
  
 <input type="submit" value="Send">
</form&gt

Supposons que le fichier send_message.php stocke tout dans une base de données afin que les propriétaires du magasin puissent lire les messages des utilisateurs ultérieurement. Il peut contenir du code comme celui-ci

<?php

$name

= $_POST['name'] ;

$message

= $_POST['message'] ;

// vérifiez si cet utilisateur a déjà un message
mysqli_query($conn, "SELECT * from messages where name = $name") ;

// Autre code ici

Vous essayez donc d’abord de voir si cet utilisateur a déjà un message non lu. La requête SELECT * from messages where name = $name semble assez simple, non ?

FAUX !

Dans notre innocence, nous avons ouvert les portes à la destruction instantanée de notre base de données. Pour que cela se produise, l’attaquant doit réunir les conditions suivantes

  • L’application fonctionne sur une base de données SQL (aujourd’hui, c’est le cas de presque toutes les applications)
  • La connexion actuelle à la base de données dispose des permissions “edit” et “delete” sur la base de données
  • Les noms des tables importantes peuvent être devinés

Le troisième point signifie que maintenant que l’attaquant sait que vous exploitez un magasin de commerce électronique, vous stockez très probablement les données relatives aux commandes dans une table ” commandes". Fort de tous ces éléments, il suffit à l’attaquant de fournir ce nom

Joe ; truncate orders;? Oui, monsieur ! Voyons ce que la requête deviendra lorsqu’elle sera exécutée par le script PHP

SELECT * FROM messages WHERE name = Joe ; truncate orders

D’accord, la première partie de la requête comporte une erreur de syntaxe (pas de guillemets autour de “Joe”), mais le point-virgule oblige le moteur MySQL à en interpréter une nouvelle : truncate orders. Comme ça, d’un seul coup, tout l’historique des commandes disparaît !

Maintenant que vous savez comment fonctionne l’injection SQL, il est temps de voir comment l’arrêter. Les deux conditions nécessaires à la réussite d’une injection SQL sont les suivantes

  1. Le script PHP doit avoir des privilèges de modification/suppression sur la base de données. Je pense que c’est vrai pour toutes les applications et que vous ne pourrez pas faire en sorte que vos applications soient en lecture seule. 🙂 Et devinez quoi, même si nous supprimons tous les privilèges de modification, l’injection SQL peut toujours permettre à quelqu’un d’exécuter des requêtes SELECT et de visualiser toute la base de données, y compris les données sensibles. En d’autres termes, réduire le niveau d’accès à la base de données ne fonctionne pas, et votre application en a quand même besoin.
  2. L’entrée de l’utilisateur est en cours de traitement. La seule façon dont l’injection SQL peut fonctionner est lorsque vous acceptez des données de la part des utilisateurs. Une fois de plus, il n’est pas pratique d’arrêter toutes les entrées de votre application simplement parce que vous craignez une injection SQL.

Prévention de l’injection SQL en PHP

Étant donné que les connexions aux bases de données, les requêtes et les entrées des utilisateurs font partie de la vie, comment prévenir les injections SQL ? Heureusement, c’est assez simple, et il y a deux façons de le faire : 1) assainir les entrées utilisateur, et 2) utiliser des instructions préparées

Assainissement des entrées utilisateur

Si vous utilisez une ancienne version de PHP (5.5 ou inférieure, ce qui arrive souvent sur les hébergements mutualisés), il est judicieux de faire passer toutes les entrées utilisateur par une fonction appelée mysql_real_escape_string(). En gros, cette fonction supprime tous les caractères spéciaux d’une chaîne de manière à ce qu’ils perdent leur signification lorsqu’ils sont utilisés par la base de données

Par exemple, si vous avez une chaîne comme Je suis une chaîne, le caractère guillemet simple (‘) peut être utilisé par un attaquant pour manipuler la requête de base de données créée et provoquer une injection SQL. En passant par mysql_real_escape_string()vous obtenez I\'m a string, qui ajoute une barre oblique inverse au guillemet simple, en l’échappant. En conséquence, la chaîne entière est transmise comme une chaîne inoffensive à la base de données, au lieu de pouvoir participer à la manipulation de la requête

Il y a un inconvénient à cette approche : c’est une très, très vieille technique qui va de pair avec les anciennes formes d’accès aux bases de données en PHP. Depuis PHP 7, cette fonction n’existe même plus, ce qui nous amène à notre prochaine solution

Utilisez les instructions préparées

Les instructions préparées sont un moyen d’effectuer des requêtes de base de données de manière plus sûre et plus fiable. L’idée est qu’au lieu d’envoyer la requête brute à la base de données, nous indiquons d’abord à la base de données la structure de la requête que nous allons envoyer. C’est ce que nous entendons par “préparation” d’une requête. Une fois la requête préparée, nous transmettons les informations sous forme d’entrées paramétrées afin que la base de données puisse “combler les lacunes” en connectant les entrées à la structure de la requête que nous avons envoyée précédemment. Cela supprime tout pouvoir spécial que les entrées pourraient avoir, ce qui fait qu’elles sont traitées comme de simples variables (ou charges utiles, si vous préférez) dans l’ensemble du processus. Voici à quoi ressemblent les instructions préparées

<?php
$servername = "localhost" ;
$username = "username" ;
$password = "password" ;
$dbname = "myDB" ;

// Créer une connexion
$conn = new mysqli($servername, $username, $password, $dbname) ;

// Vérifier la connexion
if ($conn->connect_error) {
 die("Connection failed : " . $conn->connect_error) ;
}

// préparer et lier
$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES ( ?, ?, ?)") ; $stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES ( ?, ?, ?)") ;
$stmt->bind_param("sss", $firstname, $lastname, $email) ;

// définir les paramètres et exécuter
$firstname = "John" ;
$lastname = "Doe" ;
$email = "john@example.com" ;
$stmt->execute() ;

$firstname = "Mary" ;
$lastname = "Moe" ;
$email = "mary@example.com" ;
$stmt->execute() ;

$firstname = "Julie" ;
$lastname = "Dooley" ;
$email = "julie@example.com" ;
$stmt->execute() ;

echo "New records created successfully" ;

$stmt->close() ;
$conn->close() ;
?&gt

Je sais que le processus semble inutilement complexe si vous n’avez jamais utilisé d’instructions préparées, mais le concept en vaut la peine. Voici une bonne introduction

Pour ceux qui sont déjà familiers avec l’extension PDO de PHP et qui l’utilisent pour créer des instructions préparées, j’ai un petit conseil à vous donner

Attention : Soyez prudent lorsque vous configurez PDO

Lorsque l’on utilise PDO pour accéder à une base de données, on peut avoir un faux sentiment de sécurité. “Ah, bien, j’utilise PDO. Maintenant, je n’ai plus besoin de penser à rien d’autre” — c’est ainsi que nous pensons généralement. Il est vrai que PDO (ou les instructions préparées de MySQLi) suffit à prévenir toutes sortes d’attaques par injection SQL, mais vous devez être prudent lors de sa mise en place. Il est courant de copier-coller du code provenant de tutoriels ou de vos projets antérieurs et de passer à autre chose, mais cette configuration peut tout annuler

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true)

Ce paramètre indique à PDO d’émuler les instructions préparées plutôt que d’utiliser les instructions préparées de la base de données. Par conséquent, PHP envoie de simples chaînes de requête à la base de données, même si votre code semble créer des instructions préparées, définir des paramètres et tout le reste. En d’autres termes, vous êtes aussi vulnérable à l’injection SQL qu’auparavant 🙂

La solution est simple : assurez-vous que cette émulation est réglée sur false

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false)

Maintenant, le script PHP est obligé d’utiliser des instructions préparées au niveau de la base de données, ce qui permet d’éviter toutes sortes d’injections SQL

Prévention à l’aide du WAF

Savez-vous que vous pouvez également protéger les applications web contre les injections SQL en utilisant un WAF (web application firewall) ?

En fait, il ne s’agit pas seulement de l’injection SQL, mais de nombreuses autres vulnérabilités de la couche 7, telles que les scripts intersites, l’authentification erronée, la falsification intersite, l’exposition des données, etc. Vous pouvez utiliser un WAF auto-hébergé comme Mod Security ou un WAF basé sur le cloud comme le suivant

Injection SQL et cadres PHP modernes

L’injection SQL est si courante, si facile, si frustrante et si dangereuse que tous les frameworks web PHP modernes intègrent des contre-mesures. Dans WordPress, par exemple, nous avons la fonction $wpdb->prepare(), alors que si vous utilisez un framework MVC, il fait tout le sale boulot pour vous et vous n’avez même pas à penser à prévenir l’injection SQL. C’est un peu ennuyeux que dans WordPress vous deviez préparer les déclarations explicitement, mais bon, c’est de WordPress que nous parlons 🙂

Quoi qu’il en soit, ce que je veux dire, c’est que la race moderne des développeurs web n’a pas à penser à l’injection SQL, et par conséquent, ils ne sont même pas conscients de cette possibilité. Ainsi, même s’ils laissent une porte dérobée ouverte dans leur application (peut-être un paramètre de requête $_GET et les vieilles habitudes de lancer une requête sale), les résultats peuvent être catastrophiques. Il est donc toujours préférable de prendre le temps de se plonger plus profondément dans les fondations

Conclusion

L’injection SQL est une attaque très désagréable pour une application web, mais elle peut être facilement évitée. Comme nous l’avons vu dans cet article, il suffit d’être prudent lors du traitement des entrées utilisateur (l’injection SQL n’est d’ailleurs pas la seule menace liée au traitement des entrées utilisateur) et de l’interrogation de la base de données. Cela dit, nous ne travaillons pas toujours dans la sécurité d’un framework web, il vaut donc mieux être conscient de ce type d’attaque et ne pas tomber dans le panneau.

  • Ankush
    Auteur
    J'écris sur, autour et pour l'écosystème des développeurs. Recommandations, tutoriels, discussions techniques - quoi que je publie, je fais de mon mieux pour dissiper la confusion et le flou, et fournir des réponses concrètes basées sur l'expérience personnelle... en savoir plus
Merci à nos sponsors
D'autres lectures intéressantes sur le développement
Alimentez votre entreprise
Quelques outils et services pour aider votre entreprise à se développer.
  • Invicti utilise le Proof-Based Scanning™ pour vérifier automatiquement les vulnérabilités identifiées et générer des résultats exploitables en quelques heures seulement.
    Essayez Invicti
  • Web scraping, proxy résidentiel, proxy manager, web unlocker, search engine crawler, et tout ce dont vous avez besoin pour collecter des données web.
    Essayez Brightdata
  • Monday.com est un système d'exploitation tout-en-un qui vous aide à gérer vos projets, vos tâches, votre travail, vos ventes, votre CRM, vos opérations, vos flux de travail et bien plus encore.
    Essayez le lundi
  • Intruder est un scanner de vulnérabilité en ligne qui détecte les faiblesses de votre infrastructure en matière de cybersécurité, afin d'éviter des violations de données coûteuses.
    Essayer l'intrus