Injection SQL

Injection SQL

J’ai souvent l’occasion de voir le code Php de projets de jeus amateurs en cours de réalisation. Et pratiquement à chaque fois, je me dit : « Mince, si il met son site en ligne, il ne va pas faire une semaine ! ».

Nombreux sont les amateurs qui se lancent dans le développement sans trop connaître les règles de base concernant la sécurité.
C’est bien de se lancer, il faut passer par là un jour ou l’autre sinon on ne fait rien. Mais c’est encore mieux de se lancer avec quelques notions.

Je ne vais pas vous donner toutes les solutions permettant de sécuriser un site, ce serait beaucoup trop long, je vais juste vous parler de l’injection SQL qui est un grand classique et qui reste relativement simple à parer. C’est aussi un problème que je rencontre souvent dans le code des jeux amateurs.

Comment ça marche ?

Loin de moi l’idée de vous donner ici les différentes façon de faire de l’injection SQL. Mais il est toujours bon de connaître ce contre quoi on se protège.

L’idée de base consiste à modifier des requêtes SQL suite à un formulaire. Par exemple, lorsqu’un joueur indique son identifiant et son mot de passe, une requête SQL est alors exécutée afin de vérifier si ce compte existe ou non. Le principe consiste alors à fermer la requête en indiquant un identifiant erroné puis, d’indiquer une autre requête SQL.

$SQL = 'SELECT id, pass FROM users WHERE id='$id' AND pass='$pass';

Si l’on exécute cette requête sans prendre le temps de protéger les données que contiennent les variables ‘id’ et ‘pass’, envoyées par le joueur, il est très simple de faire de l’injection SQL. Le joueur mal intentionné pourrait alors indiquer le mot de passe suivant :

zzz';
UPDATE user SET Password=PASSWORD('monpass') WHERE user='root';
FLUSH PRIVILEGES;

Le premier ‘zzz’ permet juste de clôturer la requête SQL. La suite permet de créer un utilisateur qui aura tous les privilèges sur la base et qui pourra donc sans problème supprimer, modifier ou voir les données utilisateurs.

Évidement, cela n’est pas aussi simple et il faudra à la personne mal intentionnée faire pas mal d’essais avant que ça marche. Il lui faut connaître le schéma de la base de données. Mais comme il existe des logiciels permettant de faire ces essais en masse, vous imaginez bien que ça peut aller très vite.

Comment s’en protéger ?

Plus le principe utilisé pour pirater est simple et plus la parade est simple. Mais, comme il existe pas mal de possibilités pour pirater une base, il existe aussi beaucoup de solutions à mettre en place. En voici quelques unes :

  • Ne jamais utiliser l’identifiant et le mot de passe du super utilisateur de la base. Utilisez un compte avec les droits en adéquation (pas de possibilité de modification de schéma de la base par exemple).
  • Si vous attendez un numérique, vérifiez à l’aide de la fonction is_numeric et modifiez la variable en conséquence.
  • Filtrez les entrées utilisateurs, même s’il s’agit d’un menu déroulant !!

Et comment filtrer les entrées alors ?!

Et c’est là que les créateurs amateurs devraient faire très attention. C’est un peu le nerf de la guerre.

En fonction de votre façon de coder, vous ne ferez pas la même chose. Mais il est important de séparer les fonctions de filtrage des données utilisateurs du reste de votre code. En gros, ne codez qu’une seule fois la fonction (ou la classe) de filtrage et réutilisez-la. Ceci vous permettra de la modifier qu’une seule fois si vous trouvez une meilleure solution ou si elle n’est pas adaptée au serveur. C’est une évidence pour les développeurs non débutants, mais il est bon de le rappeler pour tout ceux qui démarrent.

Votre fonction de filtrage doit donc ressembler à quelque chose comme ça :

function mon_filtre($texte=' ') {
    $texte = mysql_real_escape_string($texte);
    return $texte;
}

Attention quand même, la fonction Php « mysql_real_escape_string » n’est pas la solution a tout. N’oubliez donc pas de contrôler chaque paramètre utilisateur afin de voir si le contenu correspond bien à la demande.

La vérification de la longueur des informations saisies par utilisateur est une bonne solution pour des petits formulaire (le formulaire d’identification par exemple).

N’hésitez pas à mettre entre simple quote chaque variable dans la requête SQL, même s’il s’agit d’une variable numérique.

Il vous faudra également faire attention à la configuration de votre serveur. Si magic_quotes_gpc est activé sur votre serveur, il vous faudra utiliser la fonction stripslashes avant d’envoyer les données à mysql_real_escape_string.

Pour conclure

J’aimerais bien ne plus jamais voir de code comme j’ai eu tant l’occasion d’en voir. Ces morceaux de codes qui me permettent de penser que beaucoup de jeux n’iront pas plus loin que la première semaine de mise en route. Avec toutes les conséquences que cela induit pour le concepteur.

Voilà donc un mauvais code à ne jamais utiliser :

$SQL = "SELECT * FROM users WHERE id='$_REQUEST['id']' AND pass='$_REQUEST['pass']'";
$resultat = mysql_query($SQL);

À la place de ce code, il faudrait quelque chose de ce genre :

//---------------- fichier functions.inc.php

function getVarUtilisateur($var) {
    if(!isset($_REQUEST[$var])) {
        $retour = "";
    }else{
        $retour = trim($_REQUEST[$var]);
    }
    return $retour;
}

function protectionVar($var) {
    if (get_magic_quotes_gpc()) {
        $var = stripslashes($var);
    }

    if (!is_numeric($value)) {
        $var = mysql_real_escape_string($var);
    }
    return $var;
}

//------------------ fichier formulaire d'identification

include 'functions.inc.php';
$id = getVarUtilisateur('id');
$pass = getVarUtilisateur('pass');

//-- vérification des données (numérique, texte, longueur, ...)
//--- à faire...

$SQL = 'SELECT id, pass FROM users WHERE id='".protectionVar($id)."' AND pass='".protectionVar('$pass')."';";
$resultat = maFonctionQuerySQL($SQL);

C’est un peu simplifié évidement. À vous de créer vos propres fonctions, voir vos propres classes (ou d’utiliser des classes déjà existantes et de vous focaliser sur d’autres points).

Je ne l’indique pas ici, mais il est bon également de sécuriser le mot de passe en ne le mettant pas en clair dans la base de données (ce pourrait-être un autre article…).

Et vous, vous faites comment pour protéger vos formulaires ?! Il y aura prochainement une Radio Prélude sur ce sujet. Si vous souhaitez participer, c’est par ici que ça se passe.

PS : je vient de voir que mon blog n’est pas très adapté à l’affichage de code Php. Il faudra que je remédie à cela…