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).
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
- Code source de l’exemple,
- OWASP sur l’injection SQL,
- Le manuel de php sur l’injection SQL.