Ce tutoriel abordera les injections SQL par le biais de quelques exemples pratiques.
Il est donc souhaitable d'avoir des bases en SQL mais je m'efforcerai d'expliquer les diverses étapes de sorte que, même si vous n'avez pas ces compétences, ce tutoriel vous soit accessible.
Si vous ne savez pas ce qu'est une injection SQL, vous pouvez lire l'article d'introduction.
Ce tutoriel est à but pédagogique et n'a pas pour objectif de pousser à l'illégalité. Vous êtes donc responsable de ce que vous ferez de ces compétences. Un petit rappel des lois à ce sujet me semble opportun.
Un certain adage dans le domaine de la sécurité dit que pour mieux se protéger de son ennemi il faut le connaître et c'est dans cette optique qu'est rédigé ce tutoriel.
Les exemples qui vont suivre se rapporteront à PHP et à MySQL. Je tiens cependant à préciser que les injections SQL ne dépendent ni du langage de programmation, ni de la base de données utilisée, mais bien du fait de ne pas vérifier les données qui seront insérées dans la requête, ce qui donne la possibilité au pirate de la détourner.
Tous les chapitres fourniront :
- un code source PHP volontairement vulnérable
- un exemple d'exploitation de la vulnérabilité
Pour que ces exemples fonctionnent, il faut avoir installé un serveur web (comprenant le PHP) et le SGBDR MySQL.
WAMP (pour Windows), MAMP (pour Mac) ou LAMP (pour GNU/Linux) feront très bien l'affaire.
Ceci dit, même si vous testez ce type de faille avec les meilleures intentions du monde, les lois citées dans le lien ci-dessus sont bien d'applications. À vos risques et périls si vous testez ce genre de choses sur des sites ne vous appartenant pas, gardez bien cela en tête ! La sécurité est certes un domaine passionnant, mais il faut être conscient des risques que vous pouvez encourir si vous dépassez les limites. Par contre, s'il s'agit de vos propres projets personnels, là, bien entendu, vous en faites ce que vous voulez (à moins qu'il soit possible de porter plainte contre soi-même mais j'ai du mal à voir l'intérêt… ).
En ce qui concerne la base de données, voici la structure que nous utiliserons pour les exercices.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | CREATE DATABASE IF NOT EXISTS zds_injections_sql CHARACTER SET utf8; USE zds_injections_sql; CREATE TABLE IF NOT EXISTS users ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, username VARCHAR(20) NOT NULL, password VARCHAR(40) NOT NULL, rank TINYINT UNSIGNED NOT NULL DEFAULT 2, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS categories ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, title VARCHAR(100) NOT NULL, PRIMARY KEY(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS articles ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, title VARCHAR(100) NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, content TEXT NULL, category_id INT UNSIGNED NOT NULL, PRIMARY KEY(id), FOREIGN KEY(category_id) REFERENCES categories(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO users (username, password, rank) VALUES ('admin', 'truc', 1), ('que20', 'monsupermotdepasseintrouvable', 2), ('clementine', 'orange', 2), ('zds', 'thebestwebsite', 2); INSERT INTO categories (title) VALUES ('Web'), ('Programmation'), ('Systeme'); INSERT INTO articles (title, content, category_id) VALUES ('Mon premier article !', 'Il est pas génial mon article ? ^_^', 1), ('Mon second article', 'Et voilà le deuxième !', 1), ('Troisième article', 'Jamais deux sans trois !', 2); |
Concernant la structure des dossiers, voici ce que j'ai à la racine de mon serveur web.
Les liens se rapporteront à cette structure. Donc si vous en avez une autre, il est fort probable qu'ils ne marchent pas, à vous d'adapter.
Les injections SQL classiques
Les injections SQL en aveugle
Il n'y a pas que la sélection dans la vie !
Du SQL pour sortir de la base de données !
Comme nous avons pu le voir à travers ces différents exemples, les injections SQL sont des failles à prendre au sérieux et qui peuvent faire assez mal.
Elles peuvent malgré tout être assez facilement évitées avec un minimum de bonnes pratiques.
La préparation de requêtes est un excellent moyen de s'en prémunir (je crois que c'est supporté à peu près sur tout les SGBD actuels). Si maintenant vous ne souhaitez pas utiliser ce principe, il existe toujours les bonnes vieilles fonctions d'échappements (comme PDO::quote()), de vérifications du type de données. Cela change d'un langage à un autre mais ils possèdent généralement tous ce type de fonction.
Évitez par contre les systèmes du genre liste noire de mots-clés, car il suffit de passer à coté d'un cas (par exemple, beaucoup ignorent que le OR peut également s'écrire ||) pour que votre sécurité tombe à l'eau.
La plupart des frameworks web actuels intègrent, de base, des protections contre ces failles : ils y sont pour ainsi dire totalement immunisés (inutile donc de vous acharner sur Zeste de Savoir ). Des CMS comme Wordpress sont, généralement, plus à risque dû au fait qu'ils intègrent souvent des plugins tiers dont le code n'a parfois pas été vérifié : si ces plugins ne traitent pas correctement les données provenant de l'utilisateur et que ces données sont utilisées dans une requête, votre site peut alors être vulnérable à ce type d'injection.
En bref, ayez, déjà durant le développement, une optique de sécurité : vérifiez toujours ce qui provient de l'utilisateur, même si cela vous parait anodin, car toute donnée provenant de l'utilisateur peut avoir pour but de détourner votre application, ce qui peut avoir de fâcheuses conséquences.