Ces derniers temps, nous pouvons assister à un déluge d'affaires de piratages, assez médiatisés car touchant plusieurs millions de clients (Sony, Steam, etc). Plus récemment, nous pouvons également parler du site de l'UMP dont une simple faille de type "SQL injection" a permis aux hackers de publier une liste complète de 1000 cadres.
Il faut savoir que ce type d'attaque est très basique et de ce fait, très répandu.
On ne peut concevoir un site internet impliquant l'appel d'une base de données par l'intermédiaire de formulaires, sans penser à sécuriser les informations entrantes qui seront utilisées dans les requêtes.
Alors ? Des sites internet ou plateformes créés un peu trop vite ? Oublis, inattentions ou incompétence des stagiaires développeurs ?
Quoi qu'il en soit, pour ce post, je vous propose un bout de script PHP destiné à sécuriser la récupération de données afin d'éviter les attaques de type "SQL injection" et failles "XSS". La fonction vérifie le type en analysant la chaine contenue dans la variable en utilisant des regexp et ne retourne que ce qui est conforme au pattern.
<?php
//réplique de la fonction mysql_escape permettant de s'affranchir d'une connexion MySQL ouverte (voir php.net)
function mysql_escape_mimic($inp) {
if (is_array($inp)) return array_map(__METHOD__, $inp);
if(!empty($inp) && is_string($inp)) return str_replace(array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $inp);
return $inp;
}
//fonction permettant de filtrer les types int, float ou string
function mysql_varclean($text,$dest=null) {
if ( $dest == "var_int" ) { preg_match("/(-?[0-9]+)/i",$text,$matches); return $matches[1]?$matches[1]:0; }
elseif ( $dest == "var_float" ) { preg_match("/(-?[0-9]+[.]?[0-9]*)/i",$text,$matches); return $matches[1]?$matches[1]:0; }
elseif ( $dest == "var_string" ) return trim(mysql_escape_mimic(htmlentities($text)));
else return trim(mysql_escape_mimic(htmlentities($text)));
}
?>
//réplique de la fonction mysql_escape permettant de s'affranchir d'une connexion MySQL ouverte (voir php.net)
function mysql_escape_mimic($inp) {
if (is_array($inp)) return array_map(__METHOD__, $inp);
if(!empty($inp) && is_string($inp)) return str_replace(array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $inp);
return $inp;
}
//fonction permettant de filtrer les types int, float ou string
function mysql_varclean($text,$dest=null) {
if ( $dest == "var_int" ) { preg_match("/(-?[0-9]+)/i",$text,$matches); return $matches[1]?$matches[1]:0; }
elseif ( $dest == "var_float" ) { preg_match("/(-?[0-9]+[.]?[0-9]*)/i",$text,$matches); return $matches[1]?$matches[1]:0; }
elseif ( $dest == "var_string" ) return trim(mysql_escape_mimic(htmlentities($text)));
else return trim(mysql_escape_mimic(htmlentities($text)));
}
?>
L'envoi d'une requête MySQL de lecture, modification ou suppression s'effectue alors de la manière suivante :
<?php
$rech_auteur=strlen($_GET['auteur'])>0?$_GET['auteur']$_POST['auteur']; //récupère la donnée "auteur" en priorité sur le GET sinon POST
$rech_auteur=strlen($_GET['auteur'])>0?$_GET['auteur']$_POST['auteur']; //récupère la donnée "auteur" en priorité sur le GET sinon POST
mysql_connect("localhost", "mysql_user", "mysql_password") or die("Impossible de se connecter : " . mysql_error()); //connexion à la bdd Mysql
mysql_select_db("mydb"); //choix de la base
$result = mysql_query("SELECT `titre`, `annee` FROM `mytable` WHERE `auteur` = ".mysql_varclean($rech_auteur,"var_int")); //envoi de la requête sécurisée
while ($data = mysql_fetch_array($result)) {
mysql_select_db("mydb"); //choix de la base
$result = mysql_query("SELECT `titre`, `annee` FROM `mytable` WHERE `auteur` = ".mysql_varclean($rech_auteur,"var_int")); //envoi de la requête sécurisée
while ($data = mysql_fetch_array($result)) {
echo "Titre: ".$data['titre']." - année : ".$data['annee']." <br />";
}
mysql_free_result($result);
mysql_free_result($result);
?>
Principe de précaution (paranoïa oblige), je vous conseille d'appliquer la fonction à toutes les variables PHP incluses directement dans chacune de vos requêtes MySQL. A vous d'adapter le bon type (int, float ou string) aux variables. De cette façon, vous ne risquez aucun oubli.
Remarque : en ce qui concerne les variables de type string (sujettes aux attaques XSS), j'ai volontairement choisi d'appliquer un htmlentities() afin d'enregistrer des données saines directement dans la base de donnée MySQL. Logiquement, il aurait plutôt fallu enregistrer les données en brut puis appliquer le htmlentities() uniquement par la suite, à l'affichage, car :
- htmlentities convertit certains caractères en entité HTML et rend donc impossible l'exploitation de la base de données pour des recherches de mots clés (notamment accentués) ;
- la conversion en entité HTML peut augmenter considérablement la taille de la variable et par conséquent la taille de la table de votre base de donnée.
Aucun commentaire:
Enregistrer un commentaire