« Injections SQL et méthodes de protection » : différence entre les versions
Ligne 48 : | Ligne 48 : | ||
<<Image exemple injection dans un formulaire sans mot de passe>> |
<<Image exemple injection dans un formulaire sans mot de passe>> |
||
< |
<h3> |
||
<span style="color:green">SELECT</span> * <span style="color:green">FROM</span> Users <span style="color:green">WHERE</span> name='Alice'; <span style="color:grey">-- 'AND password = 'xxx';</span> |
<span style="color:green">SELECT</span> * <span style="color:green">FROM</span> Users <span style="color:green">WHERE</span> name='Alice'; <span style="color:grey">-- 'AND password = 'xxx';</span> |
||
</ |
</h3> |
||
Cette requète signifie qu'on souhaite se connecter avec l'utilisateurs "Alice". Cependant, on tente d'injecter du code SQL afin de pouvoir récupéer les informations d'Alice sans connaitre son mot de passe. Pour cela, on ajoute les caractères : <b>";--"</b> |
Cette requète signifie qu'on souhaite se connecter avec l'utilisateurs "Alice". Cependant, on tente d'injecter du code SQL afin de pouvoir récupéer les informations d'Alice sans connaitre son mot de passe. Pour cela, on ajoute les caractères : <b>";--"</b> |
||
Cela va avoir pour effet de terminer la requète (avec le ";" qui est le symbole de fin de requète), et commenter la suite de la requète (avec le smbole "--"). Cette requète va alors seulement récupérer les informations d'Alice et ne va pas effectuer la vérification du mot de passe, car celui-ci est en commentaire et ne sera donc pas compilé. |
Cela va avoir pour effet de terminer la requète (avec le ";" qui est le symbole de fin de requète), et commenter la suite de la requète (avec le smbole "--"). Cette requète va alors seulement récupérer les informations d'Alice et ne va pas effectuer la vérification du mot de passe, car celui-ci est en commentaire et ne sera donc pas compilé. |
||
=== Connexion avec mot de passe toujours vraie === |
=== Connexion avec mot de passe toujours vraie === |
Version du 20 novembre 2016 à 22:57
Auteurs : Rémi Rebillard et Loïc Robergeon
Les injections SQL (ou SQLi pour SQL Injection) est une une faille de sécurité dans les formulaires. On injecte dans une requête SQL, un morceau de requête qui n'est pas prévu par le système et va alors renvoyer des informations qui lui sont normalement caché, voir même les modifier.
Qu'est ce que c'est ?
Le SQL (Structured Query Language) est un langage informatique permettant d'interagir avec différente base de données relationnelles.
Il permet notamment de créer des tables dans cette base de donnée, ainsi que de les modifier.
Il est consitué de requêtes (query) qui vont interagir avec la base de donnée associée afin d'ajouter ou consulter des informations dans celle-ci.
Historique du SQL
Créé en Juin 1970 chez IBM par Donald Chamberlin et Ramond Boyce, ce langage a été conçut pour manipuler des bases de données relationnelle via des requètes.
Au départ, le nom était Structured English QUEry Language (SEQUEL) puis a été changé en SQL a cause d'un conflit de marque déposée.
C'est ensuite en 1979 qu'apparait la première version commercialement disponible de SQL dans le marché.
Il a pas la suite été reconnue comme norme internationale par l'ISO en 1987, et continue d'évoluer régulièrement depuis.
Injection SQL
Les injections SQL s’effectue principalement sur des formulaires dans des pages Web. Ces formulaires demande des informations à l'utilisateurs (login, mot de passe...), qui vont être saisie par l'utilisateur. Cette saisie est alors utilisé dans la requète SQL afin de récupérer les informations désirées par l'utilisateurs. Cette requète SQL interagie directement sur la base de donnée du serveur.
<<Image exemple formulaire>>
Cependant, certains site web sont mal configuré, et des failles apparaisse alors dans ceux-ci. Par exemple, au lieu de rentrer son login dans le précédant formulaire, on rentre d'autres informations comme :
<<Image exemple injection dans un formulaire>>
Ces caractères sont spéciaux car ils vont interagir directement sur la requète SQL lié au formulaire. Au lieu de se servir des informations présentes dans le formulaire, les informations vont modifier la requête et retourner des informations qui ne sont pas accessible a l'utilisateurs.
L'utilisateur va alors pouvoir effectuer diverse actions sur la base de donnée, comme consulter la liste des personnes présente avec leurs informations personnels, ou même la modifier.
Les différents types d’injection
Connexion sans mot de passe
<<Image exemple injection dans un formulaire sans mot de passe>>
SELECT * FROM Users WHERE name='Alice'; -- 'AND password = 'xxx';
Cette requète signifie qu'on souhaite se connecter avec l'utilisateurs "Alice". Cependant, on tente d'injecter du code SQL afin de pouvoir récupéer les informations d'Alice sans connaitre son mot de passe. Pour cela, on ajoute les caractères : ";--"
Cela va avoir pour effet de terminer la requète (avec le ";" qui est le symbole de fin de requète), et commenter la suite de la requète (avec le smbole "--"). Cette requète va alors seulement récupérer les informations d'Alice et ne va pas effectuer la vérification du mot de passe, car celui-ci est en commentaire et ne sera donc pas compilé.
Connexion avec mot de passe toujours vraie
<<Image exemple injection dans un formulaire avec mot de passe>>
SELECT * FROM Users WHERE name='Alice' AND password = 'random' or '1=1'; --;
Ici, on injecte du SQL dans le formulaire de saisie du mot de passe. En effet, on tape n'importe quelle mot de passe associé à Alice (vu qu'on ne le connait pas), puis on ajoute : or '1==1';--.
Ceci a pour effet d'ajouter une condition a notre requète. On vérifie d'abord que le mot de passe d'Alice est correcte, ce qui est bien évidement pas le cas, ou alors on regarde si 1 a bien pour valeurs 1 ce qui est toujours le cas, 1 = 1 donc va renvoyer Vraie. Grace a cette injection, la requète va alors valider la saisie du faux mot de passe, et donc récupérer toute les informations liées à Alice.
Les risques
Récupération des informations personnels
Reprennont la requète précédente :
SELECT * FROM Users WHERE name='Alice'; -- 'AND password = 'xxx';
Cette requète permet de récupérer toute les informations sur l'utilisateurs Alice.
Par ailleurs, rien ne nous empêche de récupérer les informations d'autre utilisateurs tels que Bob ou Charlie.. On a donc accès a toute les informations sur chaque utilisateurs présent dans la base de donnée.
De plus, certaine informations peuvent être confidentiel, car l'utilisateurs récupère tout, aussi bien son nom que son numéro de carte bleu.
Supression de toute la base de donnée
SELECT * FROM Users WHERE name='Alice';DROP ALL TABLES;-- 'AND password = 'xxx';
Voici un autre exemple d'injection de code SQL. Ici, on ne se préoccupe pas des saisies demandé dans les champs. Le but de cette requète est de tout détruire dans la base de donnée. Pour cela on entre un nom d'utilisateurs suivie d'un ";", ce qui va terminer la requète SQL. Puis ensuite on injecte une requète complète dans le formulaire : DROP ALL TABLES.
Cette requète a pour effet de supprimer toute les tables présentes dans la base de donnée, donc supprimer toutes les informations présentes dans la base de donnée.
Comment s’en protéger ?
Il existe plusieurs grands principes de protection contre les injections SQL.
La validation des données
La façon de se protéger des injections SQL la plus basique est la validation des données renseignées par l'utilisateur.
À l'avance, le développeur soit définir les règles qui vont gérer les données qu'il manipule. Il faut que ces règles soient respectées tout au long du processus de saisi jusqu'au stockage dans le base de données.
Une règle est dans la plupart des cas, la définition d'un certain type pour une donnée. Par exemple, on souhaite obtenir la latitude de l'utilisateur. Il faut que le développeur choisisse comment la donnée de la latitude va être stockée. Sous forme de chaîne de caractères ? Sous forme de nombre ?
Avec ce choix établi, il ne reste plus qu'à mettre en place la validation de cette donnée par rapport au type attendu.
Exemple de validation basique
- boolean : vérifier que la donnée saisie est "true" / "false", 0 ou 1
- nombre : détecter que la donnée saisie est un nombre (parsing)
L'une des données essentielle à définir au préalable est la gestion des dates. La meilleure manière de gérer les dates est de les stocker en tant que timestamp / nombre. Grâce à cela, il n'y a pas à faire attention au format de date affiché/géré. Mais il existe malgré tout des cas où la date sera stockée en tant que chaîne de caractères. Il est alors primordial de se mettre d'accord sur le format utilisé pour la date.
MM/DD/YYYY ou DD/MM/YYYY ou autre...
Devant la multitude de possibilité d'affichage d'une date, il est essentiel de le définir à l'avance. Cette validation s'applique également aux horaires qui sont tout aussi complexe à gérer que les dates.
Les chaînes de caractères
Le cas d'une chaîne de caractère est complexe. Comme le contenu de cette chaîne dépend directement de l'utilisateur, il faut définir plusieurs règles de validation afin de pouvoir la gérer au mieux.
Échappement des apostrophes
L'apostrophe est un caractère spécial dans le langage SQL. Le plus généralement, il permet d'entourer une chaîne de caractères. Si la donnée renseignée par l'utilisateur comporte un apostrophe.
Rue de l'albatros
Cette apostrophe va potentiellement poser problème lors de l'insertion dans la base de données.
Pour empécher cela, il faut "échapper" les apostrophes dans la chaîne de caractères. Cela veut dire que les apostrophes ne seront plus vu directement en tant qu'apostrophe. Pour échapper, la modification suivante va être appliquée :
' ==> \'
Cette manière d'échapper les apostrophes est souvent directement mis à disposition pour les programmeurs dans leur langage de programmation utilisé. Par exemple, en PHP, la fonction "mysqli_real_escape_string" permet d'échapper les apostrophes à notre place.
Vérifier la longueur
Lors de la validation d'une chaîne de caractères, il faut également faire attention à la longueur de cette chaîne. Lorsqu'une colonne est créée dans une base de données pour stocker une chaîne de caractères, la majorité du temps, la type VARCHAR est utilisé. Ce type de donnée force à définir une longueur maximale pour votre chaîne de caractères.
VARCHAR(64)
Cela signifie que la longueur maximale pouvant être stockée dans cette colonne pour une chaîne de caractères est de 64 caractères.
Par rapport à cette contrinte, il faut donc adapter la validation de nos données. Bien vérifier que la longueur de la chaîne renseignée par l'utilisateur ne dépasse pas cette limite ! Sinon, les informations fournies par l'utilisateur pourraient ne pas être stockées dans la base ou peut-être que la chaîne posant problème sera coupée.
Gérer les failles XSS
Les chaînes de caractères sont à l'origines d'une faille importante sur le web nommée Faille XSS. Cette faille consiste à écrire du code Javascript dans les champs texte accesibles par l'utilisateur. Si ces champs texte impliquent que les données, après soumission, seront affichées sur une page, le code Javascript renseigné sera alors interprété et des actions non voulues pourront se produire à l'encontre du site internet utilisant ces données.
Le principe de validation pour contrer les failles XSS est assez simple. Il suffit d'échapper les balises HTML afin qu'à l'affichage, les balises ne soit pas vu comme du HTML mais bien comme du texte. De cette manière, on prévient toute utilisation de balise HTML par l'utilisateur et don toute potentiel attaque XSS.
REF POUR PLUS DE DETAILS SUR LES FAILLES XSS
Les emails
Les emails sont des chaînes de caractères. Mais comme leur utilisation est bien spécifique dans le monde du web, il leur faut une règle de validation de plus. Comme leur nom l'indique, les emails servent à communiquer avec l'utilisateur par le biais de mails. Il faut donc s'assurer qu'à la saisi par l'utilisateur de son email, il soit valide / bien formé.
Le problème qui se pose avec les emails est donc de savoir valider la chaîne de caractères pour dire si c'est un email valide ou non.
La plupart des langages de programmation mettent à disposition des méthodes de validation d'email. Ces méthodes sont très bien pour les emails qui suivent un format assez standard. Mais malheureusement, il est possible que des utilisateurs aient des emails "exotiques".
Les emails listés ci-dessus sont tous valides, malgré leur syntaxe complexe. Et les fonctions mises à disposition par les langages de programmation ne sont peut-être pas assez efficaces pour détecter ces emails comme valides.
Vous vous dites certainement que vous pourriez trouver une façon de valider ces 4 emails d'une façon ou d'une autre, mais vous n'êtes pas à l'abri de ne pas pouvoir valider un email alors qu'il est valide.
La solution à ce problème est plus simple qu'il n'y parait, il suffit tout simplement de ne pas valider les emails !
Pour s'assurer que l'email renseigner par l'utilisateur est valide, il suffit de lui envoyer un mail à l'email qu'il a renseigné. Les serveurs de mail se chargeront du reste.
Par exemple, pour l'inscription d'un utilisateur sur un site. Il renseigne son email qui fera office de référence pour son compte. Pour valider son inscription, il suffit de lui envoyer un mail à l'email qu'il a renseigné. Ce mail fera office de validation de son email et lui permettra de confirmer son inscription plutôt que de tenter de détecter si son email est valide ou non.
Ce système de validation peut également être appliqué aux numéros de téléphone. Envoyer un SMS permettant de valider l'intégrité du numéro de téléphone.
Gérer vos comptes utilisateurs
Lors de la création de votre base de données, vous pouvez gérer des comptes utilisateurs. La majorité du temps, le compte utilisé pour accèder à une base de données est le compte par défaut, le compte ROOT. Ce qui peut être une vulnérabilité puisque le compte ROOT a accès en lecture, écriture à toute la base et aux tables.
Dans certains cas, il n'est pas négligeable de créer un compte utilisateur ayant seulement l'accès en lecture à certaines tables de la base de données. Grâce à cette méthode, vous restreignez l'utilisation de la base de données à seulement l'utilisation que vous voulez avoir avec le compte créé.
Les requètes paramétrées
Les requètes paramétrées sont des requètes SQL où des marqueurs de place sont mis pour s'assurer de la forme que la requète SQL aura lors de son exécution. Une requète paramétrée peut être vue comme le template d'une requète.
Exemple d'implantation
reqParam = SELECT * FROM user WHERE lastname = ? AND firstname = ?
Dans cet exemple, on crée une requète paramétrée qui retournera les informations des utilisateurs ayant un nom et un prénom précis. Pour intégrer les valeurs à cette requète, on utilise donc les marqueurs de place (placeholder) comme suit
reqParam.setParam( 1, "Alice" ); reqParam.setParam( 2, "Case" )
Les procédures stockées
Instructions SQL précompilées