Aller au contenu principal

Injection SQL

Principe de l'attaque

Une injection SQL se produit lorsqu'un attaquant peut insérer des commandes malveillantes dans une requête SQL qui sont ensuite exécutées par le système.

Dans le contexte des applications Web, l'Injection SQL est une attaque très courante où l'attaquant peut manipuler une requête SQL en injectant du code malveillant.

Une application Web vulnérable

Imaginons une application Web simple qui utilise une base de données MySQL/MariaDB pour stocker les informations d'utilisateur.

Code source de la version vulnérable

Code du fichier index.php

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

// Création de la connexion
$conn = new mysqli($servername, $username, $password, $dbname);

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

// Récupération de l'ID utilisateur depuis le formulaire
$user_id = $_POST['user_id'];

// Requête SQL vulnérable
$sql = "SELECT * FROM Users WHERE id = $user_id";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
// Affichage des données de chaque ligne
while($row = $result->fetch_assoc()) {
echo "id: " . $row["id"]. " - Name: " . $row["name"]. " " . $row["email"]. "<br>";
}
} else {
echo "0 results";
}
$conn->close();
?>

Code du fichier myDB.sql

CREATE DATABASE myDB;
USE myDB;

CREATE TABLE Users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
email VARCHAR(50),
password VARCHAR(255)
);

INSERT INTO Users (name, email, password) VALUES
('Georges Dupond', 'g.dupond@example.com', '$2y$10$eImiTXuWVxfM37uY4JANj.QjrfGPyt6wQEX/GRZCX2Bsx3LIJ1ZHO'), -- mot de passe : "password"
('Jeanne Durand', 'j.durand@example.com', '$2y$10$eImiTXuWVxfM37uY4JANj.QjrfGPyt6wQEX/GRZCX2Bsx3LIJ1ZHO'); -- mot de passe : "password"

Pour exécuter ce script SQL, vous pouvez utiliser la commande suivante dans votre terminal (remplacez username et password par vos identifiants MySQL/MariaDB) :

mysql -u username -p < script.sql

Et pour lancer le serveur PHP, utilisez la commande suivante dans le répertoire contenant votre fichier index.php :

php -S localhost:8000

Ensuite, vous pouvez ouvrir votre navigateur et aller à l'adresse http://localhost:8000/ pour voir l'application en action.

info

Le mot de passe haché dans cet exemple correspond au mot de passe "password".

Dans une application réelle, vous devez hacher les mots de passe des utilisateurs lorsqu'ils s'inscrivent ou modifient leur mot de passe. Vous pouvez utiliser la fonction password_hash() de PHP pour cela.

Déroulement de la démo (attaque possible)

Dans le code ci-dessus, si un attaquant entre quelque chose comme 1 OR 1=1 dans le champ user_id du formulaire, la requête SQL deviendra SELECT * FROM Users WHERE id = 1 OR 1=1, ce qui retournera toutes les lignes de la table Users, car 1=1 est toujours vrai.

Les étapes de l'attaque

  1. L'attaquant repère l'application vulnérable.
  2. Il insère 1 OR 1=1 dans le champ user_id.
  3. L'application exécute la requête SQL modifiée.
  4. Toutes les informations de la table Users sont renvoyées.

Correction de la vulnérabilité

Code source de la version corrigée

Pour corriger cette vulnérabilité, nous devons utiliser une requête préparée.

Ce type de requête oblige le développeur :

  • à définir d'abord toute la requête SQL,
  • puis à passer chaque paramètre à la requête.

Cela empêche un attaquant de changer le but de la requête SQL.

remarque

Le terme officiel est "requête préparée paramétrée" mais les développeurs utilisent très souvent le terme "requête préparée".

Voici le même exemple corrigé avec une requête préparée.

Code du fichier index.php

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

// Création de la connexion
$conn = new mysqli($servername, $username, $password, $dbname);

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

// Récupération de l'ID utilisateur depuis le formulaire
$user_id = $_POST['user_id'];

// Requête SQL sécurisée
$stmt = $conn->prepare("SELECT * FROM Users WHERE id = ?");
$stmt->bind_param("s", $user_id);

$stmt->execute();

$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
echo "id: " . $row["id"]. " - Name: " . $row["name"]. " " . $row["email"]. "<br>";
}

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

Déroulement de la démo (attaque impossible)

Maintenant, si un attaquant tente d'entrer 1 OR 1=1 dans le champ user_id,

la requête SQL :

  • interprète cela comme une chaîne littérale
  • et cherche un identifiant qui correspond exactement à 1 OR 1=1,

ce qui ne renverra aucun résultat.

Principe de la correction

La correction utilise des requêtes préparées.

Les valeurs sont transmises à la requête en tant que paramètres, qui sont ajoutés en toute sécurité sans modifier la construction de la requête SQL.

Risques liés à cette vulnérabilité

L'injection SQL peut permettre à un attaquant d'interroger, modifier ou même supprimer les données de votre base de données.

attention

Dans certains cas, cela peut même conduire à une élévation de privilèges si l'attaquant peut extraire des informations sensibles, telles que les mots de passe des utilisateurs ou les clés d'API.

Code de bonne conduite

  • N'utilisez jamais de données non vérifiées fournies par l'utilisateur dans une requête SQL.
  • Utilisez toujours des requêtes préparées pour prévenir l'injection SQL.
info

Les requêtes SQL préparées paramétrées présentent plusieurs avantages importants :

  1. Sécurité : L'avantage le plus important des requêtes préparées est qu'elles peuvent aider à prévenir les attaques d'injection SQL. En séparant le code SQL des données, elles empêchent un attaquant de modifier la structure de la requête SQL.

  2. Performance : Si vous exécutez une requête SQL plusieurs fois avec différents paramètres, l'utilisation de requêtes préparées peut améliorer les performances. La requête SQL n'a besoin d'être compilée qu'une seule fois, mais peut être exécutée plusieurs fois avec différentes valeurs de paramètres.

  3. Confort : Les requêtes préparées simplifient le code en évitant la nécessité d'échapper manuellement et de formater les données pour l'inclusion dans une requête SQL.

  4. Prévention des erreurs de formatage : Comme les requêtes préparées gèrent automatiquement le formatage des données, elles aident à prévenir les erreurs qui pourraient survenir lors de l'inclusion manuelle de données dans une requête SQL. Par exemple, elles évitent les problèmes liés à l'échappement incorrect des guillemets.

  5. Intégrité des données : Les requêtes préparées garantissent que les données sont traitées comme de simples données et non comme une partie du code SQL, ce qui aide à maintenir l'intégrité des données.

En résumé, l'utilisation de requêtes SQL préparées paramétrées est une pratique recommandée en matière de développement de base de données, car elle offre une meilleure sécurité, une meilleure performance et une plus grande facilité d'utilisation.

Test de mémorisation/compréhension


Qu'est-ce qu'une injection SQL ?


Comment prévenir les injections SQL ?


Quelle est la conséquence d'une injection SQL réussie ?


Dans l'exemple de code vulnérable, que se passe-t-il si un attaquant entre '1 OR 1=1' dans le champ 'user_id' ?


Qu'est-ce qu'une requête préparée ?


Que préviennent les requêtes SQL préparées paramétrées ?


Quel est l'avantage des requêtes SQL préparées paramétrées en termes de performance ?


Quel aspect des requêtes SQL préparées paramétrées aide à maintenir l'intégrité des données ?


Comment les requêtes SQL préparées paramétrées évitent-elles les erreurs de formatage ?


Qu'est-ce qui n'est PAS un avantage des requêtes SQL préparées paramétrées ?