Bonjour à tous ! Nous allons aborder dans cette vidéo la sécurisation des formulaires HTML pour contrer certaines injections SQL. Nous avons vu lors d'une vidéo précédente comment il était possible d'interroger un SGBD avec le langage SQL, en injectant des propriétés logiques toujours vraies, afin de contourner la vérification de mot de passe. Les risques liés aux injections SQL peuvent être importants voire dramatiques pour une organisation, par exemple, l'extraction d'informations sensibles, l'extraction de credentials, identifiants d'authentification tels que les logins et les mots de passe qui peuvent être utilisés sur d'autres sites internet, suppressions de données ou de table de la base de données. Ces attaques, de type injection SQL, sont possibles car les développeurs transmettent en requêtes SQL directement les entrées d'utilisateur de requêtes qui sont dites dynamiques. Pour sécuriser l'accès au SGBD, il est recommandé de ne pas utiliser de requête dynamique et/ou d'empêcher l'utilisateur d'injecter du code SQL malveillant. La première solution, radicale, qui consiste à éviter les formulaires, peut être difficile à mettre en œuvre dans certains cas : formulaire d'authentification, champ de recherche, etc. Dans ce cas, les entrées des utilisateurs sont nécessaires, mais il faut empêcher que ces entrées contiennent de l'injection de code SQL qui viendrait modifier le comportement attendu de la requête. Dans les faits, nous ne pouvons pas interdire à un utilisateur malveillant de saisir du code SQL dans un champ de formulaire. Nous n'allons donc pas l'en empêcher, mais nous allons neutraliser le code SQL malveillant. La règle à suivre est simple : ne jamais faire confiance aux informations saisies par un individu, car chaque champ de saisie dans un formulaire HTML est une porte ouverte à une injection. Nous allons donc utiliser ce que l'on nomme des Prepared Statements with Parameterized Queries, cela nous permet de conserver le comportement de la requête SQL prévue. Voyons comment nous nous servons de cette technique de sécurisation en reprenant l'exemple de la vidéo précédente. Comme nous l'avons vu, un formulaire d'authentification non sécurisé est facilement contournable. Pour rappel, injecter du code SQL dans la requête suivante : "select id, name, firstname, from Users where login=\" " .$login. "\" and pass = \" ".$mdp. "\"; " se fait facilement par la saisie de la chaîne un mot de passe" or "1"="1 et c'est donc l'injection de la propriété vraie "1"="1 dans le champ pass=""; qui contourne la vérification pass="un mot de passe"; et effectuée par le select sur le mot de passe. Il est même possible d'opérer l'injection dès le login sans fournir de nom avec guillemets or "1"="1";#" ce qui aurait donné la requête "select id, name, firstname, from Users where login=\" ;; or "1"="1";#" Sachant qu'en SQL le symbole # est utilisé pour les commentaires, tout ce qui se trouve après est commenté et l'attaquant sera authentifié. Nous allons modifier notre code PHP : l'utilisation d'une requête préparée avec PDO (PHP Data Objects), va nous servir à contrôler les entrées d'utilisateur contenues dans les variables .$login. et .$mdp. Le code devient donc le suivant : Comme nous pouvons le voir, lorsqu'une entrée utilisateur doit être insérée, nous écrivons notre requête avec des marqueurs de positionnement ? ou avec des emplacements nommés :nom et nous utilisons la fonction bindParam pour sécuriser les entrées utilisateur. Nous voyons que le type de la variable est indiqué, dans notre cas un string ou une chaîne de caractères. Mais nous pourrions aussi bien mettre entier dans d'autres requêtes, si nous n'indiquons pas de type de données, alors PDO utilisera par défaut le type chaîne de caractères, et c'est aussi PDO qui gérera l'échappement des chaînes de caractères. Dans des cas spécifiques, il peut y avoir des problèmes de performances en termes de temps d'exécution et il est donc nécessaire d'utiliser des requêtes où les entrées utilisateur sont échappés pour les sécuriser. L'échappement de caractères permet de neutraliser les caractères spéciaux pour qu'ils soient interprétés comme des caractères classiques, ici il s'agissait du guillemet qui était interprété comme fin du champ nom, ce qui faisait que tout ce qui était derrière était interprété comme du code SQL. L'échappement permet de valider les entrées utilisateurs avant de s'en servir dans la requête. Il faut noter que l'échappement des entrées utilisateur ne garantit pas l'empêchement de toutes les injections SQL et dans toutes les situations. Il est donc nécessaire d'être prudent dans cette utilisation. Les fonctions d'échappement codées à la main ne sont pas à privilégier, il est plutôt conseillé d'utiliser la fonction PHP mysqli-real-escape-string. Celle-ci échappe les caractères suivants : guillemets simple ('), guillemets double ("), antislash (\), retour chariot (\r), retour à la ligne (\n) et l'octet NUL. Si nous reprenons l'exemple précédent, mais cette fois-ci avec la fonction mysqli-real-escape-string cela donne le code suivant : ou $login = "mon champ login" et $password = "un mot de passe voir vide" or "1"="1". $safe_password contiendra un mot de passe voir vide\" or \"1\"=\"1 C'est cela qui fait que le SGBD retournera les résultats de la base de données pour lesquels le login sera "mon champ login" et le mot de passe sera exactement "un mot de passe voir vide" or "1"="1" Cette valeur de mot de passe comprenant guillemets et le chiffre 1 sera une valeur de chaîne de caractères parmi d'autres et aboutira à un échec d'authentification. L'attaque SQL ne fonctionnera pas. Il est donc cependant recommandé par l'OWASP d'utiliser les Prepared Statements et de ne se servir de l'échappement que dans les cas spécifiques où il peut y avoir un problème de performance. [AUDIO_VIDE] Dans le cadre de ce cours, depuis que nous mentionnons l'injection SQL nous parlons d'insérer du code SQL dans un formulaire HTML. Dans nos exemples, les paramètres sont envoyés à l'aide de la méthode post d'HTML, mais ce n'est pas le seul moyen d'injecter du code SQL. Il faut savoir qu'un attaquant peut appliquer des phases de reconnaissance pour choisir sa technique d'injection. Il existe des mécanismes plus hasardeux que l'injection classique, comme les Blind SQL Injections ou injections SQL en aveugle. En effet, il est possible d'utiliser l'URL de la page Web lorsque le site Web utilise la méthode Get. Pour rappel, une URL de la forme suivante utilise une méthode Get : mon_site.fr/article.php?id=22 Ici, le paramètre id est passé dans l'URL de la page, et la page article utilise sa valeur dans une requête par exemple select * from Article where auteur = 22 ; nous pouvons modifier la valeur de ce paramètre pour vérifier si le site Web est vulnérable, par exemple, en ajoutant 1=2 lors d'une première exécution vient ensuite 1=1 Le but est de vérifier s'il est possible de voir une différence entre une requête toujours fausse et une requête toujours vraie. Par exemple, mon_site.fr/article.php?id=22 and 1=2. Puis, mon_site.fr/article.php?id=22 and 1=1. Lors de ces injections, la requête ressemble à : select * from Article where auteur = 22 and 1=2, select * from Article where auteur = 22 and 1=1. Dans le premier cas, la requête ne retournera de résultat ou alors un message d'erreur. Lors de l'exécution de la 2nde requête, si le contenu de la page est différent du premier cas, alors le pirate pourra distinguer si le retour de la requête initiale était vrai ou faux. Dans ce cas, les seules limitations sont les droits fixés dans la base de données, la syntaxe SQL et l'imagination du pirate. N'oubliez pas que la plus grande limitation d'un pirate informatique, est son imagination. Il ne faut donc pas considérer qu'un site internet est suffisamment protégé parce que vous avez sécurisé toutes les attaques auxquelles vous avez pensé. Dans la métaphore du bouclier et de l'épée, nous sommes dans l'éternelle course d'adaptation du bouclier derrière les améliorations de l'épée.