Les injections SQL représentent une menace importante pour les modèles de bases de données relationnelles et les informations qui y sont stockées. Une protection complète contre ces accès externes non autorisés, rendus possibles par des failles de sécurité, est donc essentielle.

Qu’est-ce qu’une injection SQL ?

Une injection SQL est l’exploitation d’une faille de sécurité dans les systèmes de bases de données relationnelles qui utilisent le langage de requête SQL pour la saisie de données. L’attaquant utilise des entrées utilisateur non sécurisées et contenant des métacaractères comme le double tiret, les guillemets ou le point-virgule. Ces caractères ont des fonctions spéciales pour l’interpréteur SQL et permettent d’influencer de l’extérieur les commandes exécutées. L’injection SQL se produit souvent dans le cadre de programmes PHP et ASP, basés sur des interfaces obsolètes. Les entrées ne reçoivent alors parfois pas le masquage nécessaire et sont donc la cible parfaite pour une attaque.

Avec l’utilisation ciblée de caractères de fonction, un utilisateur non autorisé peut ainsi introduire d’autres commandes SQL et manipuler les entrées de telle sorte qu’il puisse modifier, effacer ou lire les données. Dans les cas les plus graves, il est même possible qu’un attaquant obtienne de cette manière l’accès à la ligne de commande du système exécutant les commandes et ainsi à l’ensemble du serveur de base de données.

Exemples d’injections SQL : comment fonctionnent les attaques de bases de données ?

Comme les serveurs de bases de données vulnérables sont rapidement détectés et que les attaques par injection SQL sont tout aussi faciles à exécuter, cette méthode est l’une des plus populaires. Les criminels utilisent différents modèles d’attaque et exploitent aussi bien des failles récentes que des vulnérabilités connues depuis longtemps dans les applications impliquées dans la gestion des données. Pour illustrer le fonctionnement exact d’une injection SQL, voici deux méthodes typiques.

Exemple 1 : accès grâce à une saisie utilisateur insuffisamment masquée

Pour que les utilisateurs puissent accéder à une base de données, ils doivent généralement s’authentifier. Il existe à cet effet des scripts qui présentent par exemple un formulaire de connexion composé d’un nom d’utilisateur et d’un mot de passe. Les utilisateurs remplissent le formulaire et le script vérifie ensuite s’il existe des entrées correspondantes dans la base de données. Par défaut, une table nommée users est créée dans la base de données, avec les colonnes username et password. Pour une application Web quelconque, les lignes de script concernées (pseudo-code Python) pour l’accès au serveur Web pourraient être les suivantes :

uname = request.POST[‘username’]
passwd = request.POST[‘password’]
sql = "SELECT id FROM users WHERE username=‘" + uname + "‘ AND password=‘" + passwd + "‘"
database.execute(sql)
python

Un attaquant a maintenant la possibilité de manipuler spécifiquement le champ mot de passe par injection SQL, en saisissant par exemple password’ OR 1=‘1, ce qui conduit à la requête SQL suivante :

sql = "SELECT id FROM users WHERE (username=‘admin’ AND password=‘‘) OR ‘1’=‘1’"
python

De cette façon, il obtient un accès complet à toute la table des utilisateurs de la base de données, puisque le mot de passe est toujours vrai (1=‘1’). Désormais, s’il se connecte en tant qu’administrateur, il peut faire toutes les modifications qu’il souhaite sur les entrées. Le champ du nom d’utilisateur est tout aussi vulnérable à ce type de manipulation.

Exemple 2 : espionner des données en manipulant l’ID

Solliciter des informations sur une base de données par authentification ID est une méthode courante. Toutefois, cela constitue une porte d’entrée pour les injections SQL. Un serveur Web peut alors savoir, grâce aux données ID transmises par l’URL, quelles informations de la base de données il doit appeler. Le script PHP correspondant ressemble au suivant :

<?php
    $mysqli = new mysqli("localhost", "user", "password", "database");
    $id = intval($_GET[‘id’]);
    $result = $mysqli->query("SELECT * FROM table WHERE id=$id");
    while ($row = $result->fetch_assoc()) {
        echo print_r($row, true);
    }
php

L’URL attendue est de la forme .../script.php?id=22. Dans ce cas précis, l’entrée de la table avec l’ID « 22 » sera appelée. Si un utilisateur externe a l’occasion de manipuler l’URL demandée et envoie au serveur Web la requête .../script.php?id=22+OR+1=1, l’appel qui en résulte entraîne la lecture de toutes les données de la table :

SELECT * FROM table WHERE id=22 OR 1=1;
sql

Comment les criminels trouvent-ils des systèmes de bases de données vulnérables ?

De manière générale, tous les sites et applications Web qui utilisent des bases de données SQL sans requêtes préparées (Prepared Statements) ou autres mécanismes de protection peuvent être vulnérables aux injections SQL. Les vulnérabilités découvertes ne restent pas longtemps secrètes dans l’immensité d’Internet ! Il existe notamment des sites d’information qui présentent les failles de sécurité actuelles et révèlent aux criminels comment trouver les projets Web vulnérables grâce à une simple recherche Google. Si un site Web renvoie des messages d’erreur SQL détaillés, les criminels peuvent les utiliser pour identifier des vulnérabilités potentielles. Il suffit d’ajouter une apostrophe à une URL contenant un paramètre d’ID, comme ici :

[Domainname].fr/news.php?id=5’

Un site Web vulnérable renverra alors un message d’erreur similaire à celui-ci :

Query failed: You have an error in your SQL syntax...

Soit en français : « Échec de la requête : Votre syntaxe SQL comporte une erreur… ». Des méthodes similaires permettent également d’afficher le nombre de colonnes, les noms des tables et des colonnes, la version SQL ou même les noms d’utilisateur et les mots de passe. Divers outils permettent en outre d’automatiser la recherche de vulnérabilités et l’exécution d’injections SQL.

Comment protéger votre base de données contre les injections SQL ?

Vous pouvez prendre différentes mesures pour empêcher les attaques par injection SQL sur votre système de base de données. Pour ce faire, vous devez examiner l’ensemble des composants impliqués : le serveur, les différentes applications ainsi que le système de gestion de base de données.

Étape 1 : surveiller les entrées automatiques des applications

Lors du traitement des entrées par des applications externes ou intégrées, il est essentiel de valider et de filtrer les valeurs transmises afin d’éviter les injections SQL.

1. Vérifier les types de données

Chaque entrée doit correspondre au type de données attendu. Par exemple, si une entrée numérique est requise, une validation simple en PHP peut ressembler à ceci :

if (filter_var($input, FILTER_VALIDATE_INT) === false) {
    throw new InvalidArgumentException("Entrée invalide");
}
php

Des contrôles similaires doivent être implémentés pour les chaînes de caractères, les valeurs de date ou d’autres formats spécifiques.

2. Filtrer les caractères spéciaux

Les caractères spéciaux peuvent être à l’origine de failles de sécurité, en particulier dans les contextes SQL ou HTML. Une méthode sûre est d’utiliser htmlspecialchars() pour les entrées HTML et PDO::quote() pour les requêtes SQL.

3. Éviter les messages d’erreur

Les messages d’erreur directs détaillant des informations techniques sur la base de données ou le système doivent être évités. Au lieu de cela, il est recommandé d’utiliser une sortie générique comme :

echo "Une erreur est survenue. Veuillez réessayer plus tard.";
error_log("Une erreur inattendue s’est produite. Pour plus de détails, consultez le journal système.");
php

4. Utiliser des requêtes préparées

Un moyen sûr d’éviter les injections SQL est d’utiliser les requêtes préparées mentionnées précédemment. Avec cette approche, les commandes SQL et les paramètres sont traités séparément, ce qui empêche l’exécution de code malveillant. Voici un exemple adapté en PHP avec PDO (PHP Data Objects) :

$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->bindParam(‘:id’, $user_id, PDO::PARAM_INT);
$stmt->execute();
php

Le système de gestion de base de données assure automatiquement le traitement sécurisé des entrées.

Étape 2 : assurer une protection complète du serveur

La sécurité du serveur sur lequel vous exécutez votre système de gestion de base de données joue un rôle important dans la prévention des injections SQL. La première étape consiste à renforcer votre système d’exploitation selon le schéma établi :

  • Installez ou activez uniquement les applications et les services pertinents pour le fonctionnement de la base de données.
  • Supprimez tous les comptes utilisateurs dont vous n’avez pas besoin.
  • Assurez-vous que toutes les mises à jour pertinentes du système et des programmes sont installées.
  • Appliquez le principe du moindre privilège afin de vous assurer que les utilisateurs et les services ne disposent que des autorisations strictement nécessaires.

En fonction des exigences de sécurité de votre projet Web, des mesures de protection supplémentaires peuvent être envisagées :

  • Systèmes de détection d’intrusion (IDS) et systèmes de prévention d’intrusion (IPS) : ces systèmes utilisent différentes méthodes de détection pour détecter les attaques sur le serveur à un stade précoce, émettre des alertes et, dans le cas de l’IPS, déclencher automatiquement les contre-mesures appropriées.
  • Application Layer Gateway (ALG) : un ALG surveille et filtre le trafic entre les applications et les navigateurs Web directement au niveau de l’application.
  • Web Application Firewall (WAF) : un WAF protège les applications Web de manière ciblée contre l’injection SQL et le Cross-Site-Scripting (XSS) en bloquant ou en neutralisant les requêtes suspectes.
  • Approche Zero Trust : cette approche moderne de la sécurité garantit que chaque accès, quelle que soit sa source, est contrôlé et vérifié avant d’être autorisé.
  • Réglementation du pare-feu et segmentation du réseau : ces mesures sont essentielles pour minimiser la surface d’attaque à long terme.
  • Audits réguliers de sécurité informatique et tests d’intrusion : ils permettent d’identifier et de combler les vulnérabilités à un stade précoce.

Étape 3 : durcir la base de données et utiliser des codes sécurisés

Tout comme votre système d’exploitation, la base de données doit être débarrassée de tous les éléments superflus et mise à jour régulièrement. Supprimez toutes les procédures stockées inutilisées et désactivez tous les services et comptes utilisateurs inutiles. Créez un compte dédié à la base de données, exclusivement réservé à un accès Web, avec des droits d’accès limités.

Dans l’esprit des requêtes préparées, il est fortement recommandé de ne pas utiliser le module PHP mysql (supprimé depuis PHP 7) et de choisir à la place mysqli ou PDO. Ces alternatives permettent d’écrire du code plus sécurisé. Voici un exemple de requête sécurisée avec mysqli :

$mysqli = new mysqli("localhost", "user", "password", "database");
if ($mysqli->connect_error) die("Echec de la connexion");
$stmt = $mysqli->prepare("SELECT password FROM users WHERE username = ?");
$stmt->bind_param("s", $_POST[‘username’]);
$stmt->execute();
$stmt->bind_result($hashedPassword);
if ($stmt->fetch() && password_verify($_POST[‘password’], $hashedPassword)) {
    echo "Connexion réussie";
} else {
    echo "Identifiants de connexion erronés";
}
$stmt->close();
$mysqli->close();
php

De plus, les mots de passe ne doivent jamais être stockés directement dans une base de données ou demandés en texte brut. Utilisez plutôt une méthode de hachage comme password_hash() en combinaison avec password_verify() pour les protéger efficacement, par exemple de la manière suivante :

$mysqli = new mysqli("localhost", "user", "password", "database");
$stmt = $mysqli->prepare("SELECT password FROM users WHERE username = ?");
$stmt->bind_param("s", $_POST[‘username’]);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
if ($row && password_verify($_POST[‘password’], $row[‘password’])) {
    echo "Connexion réussie !";
} else {
    echo "Nom d’utilisateur ou mot de passe erroné.";
}
php

Bobby Tables : les injections SQL expliquées en BD

Sur le site bobby-tables.com, la bande dessinée xkcd aborde le problème des entrées utilisateur non sécurisées dans les bases de données. Elle met en scène une mère qui reçoit un coup de fil de l’école de son fils (affectueusement surnommé le petit Bobby Tables). Elle confirme que son fils s’appelle bel et bien Robert’); DROP TABLE Students;-- et comprend la raison de l’appel : après que l’école ait essayé de créer une entrée pour Robert dans la base de données, toutes les données existantes ont été effacées. La mère de Robert ne s’en émeut pas, mais exprime plutôt son souhait que l’école apprenne de cette erreur et prenne des mesures pour assainir les entrées de leurs bases de données à l’avenir.

La bande dessinée illustre clairement les conséquences dramatiques que peuvent avoir les entrées non vérifiées des utilisateurs dans les bases de données.

Cet article vous a-t-il été utile ?
Aller au menu principal