Injections SQL : principes et analyse des vulnérabilités courantes

Principe des injections SQL

Une vulnérabilité d'injection SQL survient lorsque deux conditions sont réunies : l'utilisateur peut contrôler les paramètres d'entrée, et ces paramètres sont directement incoroprés dans une requête SQL sans validation. Cela permet à un attaquant de manipuler la requête pour extraire ou modifier des données. Il est essentiel de traiter toute donnée externe comme non fiable.

Injection par union

Cette technique exploite les requêtes SELECT pour extraire des données supplémentaires. Voici un exemple de code vulnérable :

<?php
$connection = new mysqli("localhost", "admin", "securePass123", "webapp");
if ($connection->connect_error) {
    die("Échec de connexion: " . $connection->connect_error);
}
$userInput = $_GET['param'];
$query = "SELECT * FROM accounts WHERE id = " . $userInput;
$result = $connection->query($query);
if ($result) {
    $data = $result->fetch_assoc();
    echo $data['username'] . " : " . $data['hash'];
}
?>

Ici, le paramètre param est directement concaténé sans filtrage, permettant une ijnection via UNION pour récupérer d'autres tables. L'attaquant peut déterminer le nombre de colonnes et utiliser des requêtes comme UNION SELECT username, password FROM admins.

Injection booléenne à l'aveugle

Lorsque les résultats ne sont pas affichés, mais que des messages de réponse binaire (oui/non) sont renvoyés, on peut utiliser des conditions booléennes. Exemple de code :

<?php
$pdo = new PDO("mysql:host=localhost;dbname=shop", "user", "pass123");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$entry = $_GET['item'];
if (preg_match('/benchmark|sleep/i', $entry)) {
    exit("Interdit");
}
$stmt = $pdo->prepare("SELECT * FROM products WHERE category = ?");
$stmt->execute([$entry]);
echo $stmt->fetch() ? "Trouvé" : "Introuvable";
?>

L'attaquant peut inférer des informations en testant des conditions comme AND LENGTH(database()) > 5 et observer la réponse. En itérant sur chaque caractère, il peut reconstruire des noms de base de données, tables ou colonnes.

Injection par erreur

Certaines applications affichent les messages d'erreur SQL, ce qui peut être exploité pour extraire des données. Exemple :

<?php
$conn = mysqli_connect("dbhost", "dbuser", "dbpass", "mydb");
if (!$conn) {
    die("Erreur de connexion");
}
$inputId = $_GET['search'];
$result = mysqli_query($conn, "SELECT * FROM customers WHERE id = " . $inputId);
if (!$result) {
    echo "Erreur: " . mysqli_error($conn);
}
?>

En injectant des fonctions comme UPDATEXML, l'attaquant force l'affichage d'informations. Par exemple, AND UPDATEXML(1, CONCAT(0x7e, (SELECT USER()), 0x7e), 1) peut révéler l'utilisateur courant dans le message d'erreur.

Injection temporelle à l'aveugle

Quand aucune réponse directe n'est visible, on peut utiliser des délais pour inférer des données. Code exemple :

<?php
$db = new mysqli("localhost", "root", "password", "auth");
if ($db->connect_error) {
    exit("Connexion échouée");
}
$param = $_GET['id'];
if (preg_match('/union/i', $param)) {
    exit("Requête non autorisée");
}
$sql = "SELECT * FROM users WHERE id = " . $param;
$res = $db->query($sql);
echo $res->num_rows > 0 ? "Vrai" : "Faux";
?>

L'attaquant utilise des fonctions comme SLEEP() ou BENCHMARK() combinées avec IF pour mesurer le temps de réponse. Par exemple, IF(LENGTH(database()) = 5, SLEEP(5), 1) indique une base de données de longueur 5 si le délai est de 5 secondes.

Étiquettes: SQL Injection PHP MySQL Web Security blind injection

Publié le 16 juillet à 12h03