Injections SQL

Ne jamais se fier au client

Toutes les données en provenance du client :

  • Entêtes HTTP,
  • Paramètres de l’URL, query string
  • Corps de la requête, données des formulaires,
  • Cookies, Storage API,

peuvent contenir des valeurs non valides, pour plusieurs raisons :

  • L’utilisateur a fait une erreur de saisie ;
  • Le client n’utilise pas JavaScript ;
  • Le client est un robot ;
  • L’utilisateur est un hacker qui cible votre site.

Pour toutes ces raisons, le code du serveur doit toujours vérifier les données envoyées par le client.

Injections SQL

Considérez le code suivant, qui vérifie la connexion d’un utilisateur.

$user = $req->request->get('user');
$pass = $req->request->get('pass');
$sql = "SELECT * FROM users WHERE login='$user' AND password='$pass'";
if ($app['db']->fetchAssoc($sql)) {
  // utilisateur connecté
}

L’utilisateur envoie les paramètres suivants dans le corps de la requête :

user=root
pass=' OR '1'='1

La chaîne $sql vaudra alors

SELECT * FROM users WHERE login='root' AND password='' OR '1'='1'

La condition est toujours vérifiée : le hacker est connecté en tant que root!

Que peut-on faire avec les injections SQL ?

  • Escalade de droits (se faire passer pour root),
  • Vol de données (lire la base),
  • Compromission de la base de données (effacer/modifier les données).

https://xkcd.com

Et voici une liste de attaques par injection SQL documentées.

Note : bien que réaliste, l’attaque suggérée par XKCD est improbable : aussi bien PHP que Node.js interdisent par défaut les statements multiples.

Contrer les injections SQL

On connaît la solution : échapper les caractères spéciaux', ", ;

  • PHP : mysqli::real_escape_string,
  • PDO/Doctrine : PDO::quote, Doctrine::quote,
  • Échappement automatique : requêtes préparées.

Exemple

$app['db']->fetchAssoc("SELECT * FROM users WHERE login=? AND password=?",
                       array($user, $pass));

Résultat

SELECT * FROM users WHERE login='root' AND password=''' OR ''1''=''1'

Lectures

Fork me on GitHub