Se protéger des attaques par injection SQL

Si les pages de votre site dépendent de la valeur d’une variable afin de pouvoir afficher son contenu qui est stocké dans une base de donnée (pratique standard dans un site web dynamique de type PHP et MySQL), comme dans l’exemple typique suivant:

http://www.example.com/page.php?id=1

Ou si vous dépendez d’une valeur inscrite par un utilisateur pour consulter votre base de donnée, comme avec un système de login, vous êtes vulnérables à une attaque par injection SQL à moins de vérifier la valeur de cette variable pour chaque requête et d’utiliser des méthodes de protection.

Cette vulnérabilité peut compromettre la sécurité de votre site, de vos utilisateurs, et peut même entrainer la perte de données importantes, ce qui est troublant si votre site dépends en plus de la contribution des utilisateurs et que vous ne faites pas de copies de sauvegarde courantes… des jours, voir des semaines, de contenu peuvent être perdu à jamais.

Une attaque consiste à ajouter des commandes propres à la base de donnée à l’intérieur de la valeur (une « injection ») qui est utilisée de façon à dérouter la fonction SQL de sa tâche originale. Par exemple, une variable qui devrait contenir un simple chiffre qui identifie une rangée dans la base de donnée pourrait contenir en plus une fonction additionnelle qui efface la table au complet.

Quelques méthodes existent pour contrer ces exploits, mais une essentielle est d’évaluer la valeur d’une variable avec une expression rationnelle (regular expression, en anglais) pour s’assurer qu’elle contient véritablement ce qu’on attends d’elle, sans plus.

Dans le cas de notre premier exemple, on s’attend évidemment à ce que la valeur soit un chiffre, nous allons donc vérifier si c’est le cas, avant même de consulter la base de donnée MySQL avec une opération SELECT:

$id = $_GET["id"];
if ((!$id) || (!ereg("^[0-9]+$", $id))) exit("Pas de page à cette addresse");

Si la valeur n’existe pas, ou si la valeur ne correspond pas à un chiffre, au début et à la fin (indiqué par les symboles ^ et $), on sort du programme pour afficher une erreur.

On peut ensuite utiliser la variable en toute sécurité dans la fonction MySQL suivante:

$query = "SELECT * FROM Articles WHERE ID = " . $id;

Un autre exemple, cette fois, avec un paramètre littéral en majuscule:

http://www.example.com/page.php?id=ABC

Donc:

$id = $_GET["id"];
if ((!$id) || (!ereg("^[A-Z]+$", $id))) exit("Pas de page à cette addresse");

Si la valeur ne correspond pas à un mot tout en majuscule, sans espace ou autre caractère non permis, on sort du programme.

Dans les cas où on accepte des valeurs inscrites par les visiteurs, comme dans l’entrée d’un nom d’utilisateur et de son mot de passe, il est plus difficile de construire une expression rationnelle, car elle pourrait devenir assez complexe (mais si vous le pouvez, faites le), toutefois on devrait au moins se protéger contre l’emploi de caractères propres à MySQL qui sont souvent utilisés dans le cas d’attaque par injection, comme NULL, \x1a, \n, \r, \, ‘,  » et \x00.

Dans ce cas, on recommande la fonction PHP mysql_real_escape_string() sur ces variables avant des les utiliser dans une requête auprès de la base de donnée.

Exemple:

$utilisateur = mysql_real_escape_string($_POST["utilisateur"]);
$query = "SELECT * FROM Utilisateurs WHERE utilisateur = '" . $utilisateur  . "'";

Cette fonction « échappe » (escape, en anglais) les caractères conflictuels avec une barre d’échappement \ afin de considérer leur valeur littérale plutôt que leur valeur comme caractère d’opération MySQL. Cette commande doit par contre être appelée après qu’une connexion MySQL ait été établie.

Il faut aussi s’assurer que PHP ne fonctionne pas en mode « Register Globals » (qui est maintenant désuet) et que la valeur des variables soient toujours acquise selon le procédé prévu, comme suit:

Pour les formulaires:

$variable = $_POST["variable"];

Pour les variables dans le URL:

$variable = $_GET["variable"];

De cette façon, un formulaire dont le type d’action est en mode POST ne devrait pas recueillir la valeur des variables si elles sont envoyées en mode GET, le procédé standard pour les injections SQL.

Mise à jour

J’ai récemment découvert un module Firefox qui permets de tester les vulnérabilités aux injection SQL sur son propre site, voici le lien: SQL Inject Me.

7 thoughts on “Se protéger des attaques par injection SQL

  1. Mercii bien pour ces conseil, ils sont très utiles.
    je vais les utiliser pour mon site

  2. un truc tout bête quant on attend une valeur numérique :

    $variable = $_POST[« variable »] +0;

    le +0 force la conversion en numérique et tue ce qui ne doit pas y être

  3. Bonjour !

    Même si cet article n’est plus tout récent, je suis tombé dessus dans les premiers liens de ma recherche google, et je tiens à faire une petite remarque sur un passage :

    <>

    Je me dois de prévenir nos nouveaux programmeurs, les injections SQL peuvent venir par GET, mais aussi par méthode POST, via les cookies, et même dans certains cas en forcant la valeur initiale des variables PHP si elles ne sont pas correctement initialisés en début de page. Et c’est possible qu’il y ait d’autres procédés qui me sont incconus …
    Le petit plugin bien utile de firefox test à ma connaissance une petite partie (probablement POST et GET), mais faites attention à TOUTES les variables que vous recevez de l’utilisateur si vous voulez les utilisez.

    Certains sites possèdent des failles de sécurité uniquement en écrivant le type de votre navigateur en bas de page sans l’avoir préalablement vérifier…

    Bon code !
    Lural

  4. Bon apparemment le double chevron n’ai pas apprécié …

    Le passage était donc :

    […]envoyées en mode GET, le procédé standard pour les injections SQL.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *