MySQL - et injection SQL

Si vous prenez une entrée utilisateur via une page Web et l'insérez dans une base de données MySQL, il est possible que vous vous soyez laissé ouvert à un problème de sécurité appelé SQL Injection. Ce chapitre vous apprendra comment éviter que cela ne se produise et vous aidera à sécuriser vos scripts et vos instructions MySQL.

L'injection SQL se produit généralement lorsque vous demandez à un utilisateur une entrée, comme son nom et au lieu d'un nom, il vous donne une instruction MySQL que vous exécuterez sans le savoir sur votre base de données.

Ne faites jamais confiance aux données fournies par un utilisateur, ne traitez ces données qu'après validation; en règle générale, cela se fait par correspondance de modèles. Dans l'exemple suivant, le nom d'utilisateur est limité aux caractères alphanumériques plus le trait de soulignement et à une longueur comprise entre 8 et 20 caractères - modifiez ces règles si nécessaire.

if (preg_match("/^\w{8,20}$/", $_GET['username'], $matches)) {
   $result = mysql_query("SELECT * FROM users WHERE username = $matches[0]");
} else  {
   echo "username not accepted";
}

Pour illustrer ce problème, considérez l'extrait suivant.

// supposed input
$name = "Qadir'; DELETE FROM users;";
mysql_query("SELECT * FROM users WHERE name = '{$name}'");

L'appel de fonction est censé récupérer un enregistrement de la table users, où la colonne name correspond au nom spécifié par l'utilisateur. Dans des circonstances normales, $ name ne contiendrait que des caractères alphanumériques et peut-être des espaces. Mais ici, en ajoutant une toute nouvelle requête à$name, l'appel à la base de données se transforme en catastrophe. La requête DELETE injectée supprime tous les enregistrements des utilisateurs.

Heureusement, si vous utilisez MySQL, le mysql_query()ne permet pas l'empilement de requêtes ou l'exécution de plusieurs requêtes en un seul appel de fonction. Si vous essayez d'empiler des requêtes, l'appel échoue.

Cependant, d'autres extensions de base de données PHP, telles que SQLite et PostgreSQL, exécutez avec plaisir des requêtes empilées, exécutant toutes les requêtes fournies dans une chaîne et créant un problème de sécurité sérieux.

Empêcher l'injection SQL

Vous pouvez gérer tous les caractères d'échappement intelligemment dans des langages de script comme PERL et PHP. L'extension MySQL pour PHP fournit la fonctionmysql_real_escape_string() pour échapper les caractères d'entrée spécifiques à MySQL.

if (get_magic_quotes_gpc()) {
   $name = stripslashes($name);
}

$name = mysql_real_escape_string($name);
mysql_query("SELECT * FROM users WHERE name = '{$name}'");

Le LIKE Quandary

Pour résoudre le dilemme LIKE, un mécanisme d'échappement personnalisé doit convertir les caractères% et _ fournis par l'utilisateur en littéraux. Utilisationaddcslashes(), une fonction qui vous permet de spécifier une plage de caractères à échapper.

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