MySQL et l'injection SQL

Si vous obtenez des données de saisie utilisateur via une page web et les insérez dans une base de données MySQL, des problèmes de sécurité d'injection SQL peuvent survenir.

Ce qu'on appelle l'injection SQL est le processus consistant à insérer des commandes SQL dans des chaînes de requête soumises via des formulaires web ou saisies dans des noms de domaine ou des requêtes de page, ce qui finit par tromper le serveur pour qu'il exécute des commandes SQL malveillantes.

L'injection MySQL fait référence à un attaquant qui exécute avec succès des requêtes SQL malveillantes au moyen d'une entrée malveillante. Cela se produit généralement lorsque l'entrée utilisateur n'est pas correctement validée ou échappée. L'attaquant tente d'insérer du code SQL dans l'entrée pour exécuter des requêtes non intentionnelles ou endommager la base de données.

Nous ne devrions jamais faire confiance aux entrées utilisateur. Nous devons supposer que toutes les données d'entrée utilisateur ne sont pas sécurisées, et nous devons filtrer et traiter toutes les données d'entrée utilisateur.

Supposons qu'il existe un système de connexion où les utilisateurs s'authentifient en saisissant un nom d'utilisateur et un mot de passe :

SELECT * FROM users WHERE username = 'input_username' AND password = 'input_password';

Sans validation appropriée des entrées et sans mesures préventives, un attaquant peut saisir un nom d'utilisateur similaire à ce qui suit :

' OR '1'='1'; --

Dans ce cas, la requête SQL devient :

SELECT * FROM users WHERE username = '' OR '1'='1'; --' AND password = 'input_password';

Cela fait que la requête renvoie tous les utilisateurs car1=1est toujours vrai, et le symbole de commentaire -- est utilisé pour mettre en commentaire le reste de la requête d'origine afin de garantir une syntaxe correcte.

Prévention de l'injection SQL :

  • Utilisez des requêtes paramétrées ou des instructions préparées :L'utilisation de requêtes paramétrées (Prepared Statements) peut efficacement prévenir l'injection SQL car elles séparent les données d'entrée de l'instruction de requête avant d'exécuter la requête.

  • Validation et échappement des entrées :Validez correctement les entrées utilisateur et utilisez des fonctions d'échappement appropriées (telles quemysqli_real_escape_string) pour traiter l'entrée et empêcher une injection malveillante.

  • Principe du moindre privilège :Accordez aux utilisateurs de la base de données le moins de privilèges possible, en veillant à ce qu'ils ne puissent effectuer que les opérations nécessaires afin de réduire les dommages potentiels.

  • Utiliser des frameworks ORM :L'utilisation de frameworks de mappage objet-relationnel (ORM) (tels que Hibernate, Sequelize) peut aider à abstraire les requêtes SQL, réduisant ainsi le risque d'injection SQL.

  • Désactiver l'affichage des messages d'erreur :Dans les environnements de production, désactivez l'affichage des messages d'erreur détaillés pour empêcher les attaquants d'obtenir des informations sensibles sur la structure de la base de données.


Exemples d'application

Dans l'exemple suivant, le nom d'utilisateur saisi doit être une combinaison de lettres, de chiffres et de traits de soulignement, et la longueur du nom d'utilisateur doit être comprise entre 8 et 20 caractères :

Exemple

IF (preg_match("/^\w{8,20}$/", $_GET['username'], $matches)){
   $result = mysqli_query($conn, "SELECT * FROM users
                          WHERE username=$matches[0]"
);
}
 ELSE
{
   echo "Invalid username input";
}

Regardons la situation SQL qui se produit lorsque les caractères spéciaux ne sont pas filtrés :

// 设定$name 中插入了我们不需要的SQL语句
$name = "Qadir'; DELETE FROM users;";
 mysqli_query($conn, "SELECT * FROM users WHERE name='{$name}'");

Dans l'instruction d'injection ci-dessus, nous n'avons pas filtré la variable $name. Une instruction SQL indésirable a été insérée dans $name, ce qui supprimera toutes les données de la table users.

En PHP, mysqli_query() ne permet pas d'exécuter plusieurs instructions SQL, mais dans SQLite et PostgreSQL, plusieurs instructions SQL peuvent être exécutées en même temps. Par conséquent, nous devons valider strictement les données pour ces utilisateurs.

Pour prévenir l'injection SQL, nous devons prêter attention aux points clés suivants :

  • 1. Ne faites jamais confiance aux entrées utilisateur-- Validez les entrées utilisateur à l'aide d'expressions régulières, en limitant la longueur, en échappant les guillemets simples, les guillemets doubles, etc.
  • 2. N'utilisez jamais de concaténation SQL dynamique-- Utilisez du SQL paramétré ou directement des procédures stockées pour l'interrogation et le stockage des données.
  • 3. N'utilisez jamais de connexions à la base de données avec des privilèges d'administrateur-- Utilisez une connexion distincte à la base de données avec des privilèges limités pour chaque application.
  • 4. Ne stockez pas directement les informations confidentielles-- Utilisez un hachage pour chiffrer les mots de passe et les informations sensibles.
  • 5. Les messages d'exception de l'application doivent fournir le moins d'informations possible-- Il est préférable d'utiliser des messages d'erreur personnalisés pour encapsuler les messages d'erreur d'origine.
  • 6. Les méthodes de détection d'injection SQL utilisent généralement des logiciels auxiliaires ou des plateformes web pour la détection-- Utilisez des outils spécialisés d'analyse de vulnérabilités (tels que sqlmap, Acunetix, Netsparker) pour effectuer une détection automatisée d'injection SQL sur les applications.

Prévention de l'injection SQL

Dans les langages de script tels que Perl et PHP, vous pouvez échapper les données d'entrée utilisateur pour prévenir l'injection SQL.

L'extension MySQL de PHP fournit la fonction mysqli_real_escape_string() pour échapper les caractères d'entrée spéciaux.

Exemple

IF (get_magic_quotes_gpc())  {
  $name = stripslashes($name);
}
$name = mysqli_real_escape_string($conn, $name);
 mysqli_query($conn, "SELECT * FROM users WHERE name='{$name}'");

Injection dans les instructions LIKE

Dans une requête LIKE, si la valeur saisie par l'utilisateur contient_et%, la situation suivante peut se produire : l'utilisateur voulait initialement uniquement rechercherabcd_, mais les résultats de la requête incluent "abcd_"、"abcde"、"abcdf"et ainsi de suite ; des problèmes se produisent également lorsque l'utilisateur veut rechercher "30%" (note : trente pour cent).

Dans les scripts PHP, nous pouvons utiliser la fonction addcslashes() pour gérer les situations ci-dessus, comme dans l'exemple suivant :

Exemple

$sub = addcslashes(mysqli_real_escape_string($conn, "%something_"), "%_");
// $sub == \%something\_
 mysqli_query($conn, "SELECT * FROM messages WHERE subject LIKE '{$sub}%'");

La fonction addcslashes() ajoute des barres obliques inverses devant les caractères spécifiés.

Syntaxe :

addcslashes(string,characters)
Paramètre Description
string Obligatoire. Spécifie la chaîne à vérifier.
caractères Facultatif. Spécifie les caractères ou les plages de caractères affectés par addcslashes().

Pour des applications spécifiques, voir :Fonction PHP addcslashes()

Autres extensions