Forum Programmation.php Éviter les Injections SQL

Posté par  .
Étiquettes : aucune
0
22
mar.
2005
Bonjour à tous,

Tout d'abord, un petit lien pour sensibiliser les devs php au douloureux problème des injections SQL : http://www.php.net/manual/fr/security.database.sql-injection.php(...)

L'exemple 27-4 fait froid dans le dos : au lieu d'afficher le nom du produit, le hacker réussi à afficher tous les logins/mot de passes stockés dans la base!

Afin de remédier à ce problème, j'ai codé trois petites fonctions qui permettraient d'empecher ça :


function coreSecureString ($stringValue) {
//On isole tout ce qui pourrait être derrière un espace,
//pour ne garder que le premier mot
$newString = explode(" ", $stringValue);
return $newString[0];
}

function coreSecureQuery ($Query, $valuesArray) {
$securedValuesArray = array ();
//On récupère la requête dans le tableau
$securedValuesArray[] = $Query;
//Pour chaque valeur envoyée, on ajoute des \ devant les " et les '
foreach ($valuesArray as $value) {
$securedValuesArray[] = addslashes ($value);
}
$securedQuery = call_user_func_array ("sprintf", $securedValuesArray);
return $securedQuery;
}

function coreSecureInteger ($integerValue) {
//Super simple : on récupère un entier à tous les coups
if (settype ($integerValue, "integer"))
return $integerValue;
else die ("$integerValue is not a valid integer. Execution aborted");
}


pour la fonction coreSecureString(), on part du principe qu'il y a forcément des espaces dans une injection SQL. On récupère donc le premier mot de la chaine, et on oublie tout le reste.

La fonction coreSecureQuery() mixe une requete avec le tableau de ses arguments, après que chacun d'entre eux aient été "sécurisés" avec la fonction addslashes (), cette dernière ajoutant des \ avant chaque ' ou ". Je pense que cette fonction prend tout son sens dans le cadre de chaines de caractères longues envoyées par l'internaute, comme un message posté dans un forum à l'aide d'un formulaire de saisie.

La fonction coreSecureInteger () est terriblement efficace : si l'argument commence par un nombre suivi d'une chaine de caractère, coreSecureInteger () renverra seulement le nombre. Si l'argument commence par un caractère, elle renvoie 0.

Un script pour tester tout ça :


<?php

//Nos fonctions sont stockées dans un fichier à part
include 'includes/lib.php';

/*********************************
On part du principe que la table 'datas' contient les champs suivants :

- id integer
- category VARCHAR(32)
- member integer
- content text

*********************************/
$sql = "SELECT * from datas where member = %s and category = \"%s\"";

//Le premier argument est un entier, on le sécurise
$securedInt = coreSecureInteger ($_GET['arg1']);
$arg = array ($securedInt, $_GET['arg2']);

//On génère une requête SQL sécurisée
$securedSQL = coreSecureQuery ($sql, $arg);
echo $securedSQL;

/******************************
config.php assure la connection à la base
On va utiliser MySQL pour l'exemple
******************************/
include 'includes/config.php'

//Instant critique : on envoie la requête au serveur
$handle = mysql_query ($securedSQL);
echo mysql_error ();


//On affiche les infos retournées par le serveur
while ($loadedDatas = mysql_fetch_array ($handle)) {
echo $loadedDatas[0] ...
}


?>


J'ai tenté pas mal de possibilités pour injecter du code malveillant, je n'ai pas réussi. Mais je ne suis pas sûr à 100% que ce code soit vraiment sécurisé, il y aurait-il des volontaires pour m'aider à le rendre vraiment sûre?

Merci d'avance.
  • # Proteger les champ

    Posté par  . Évalué à 3.

    Il suffit simplement de proteger ses champs et d'"escaper" les carateres de fin de chaine SQL.

    Dam
    • [^] # Re: Proteger les champ

      Posté par  . Évalué à 1.

      Pour "escaper" la chaine SQL en MySQL, on peut utiliser la fonction suivante : mysql_real_escape_string () ( http://www.php.net/manual/fr/function.mysql-real-escape-string.php)(...)

      Le problème, c'est lorsqu'on développe avec autre chose que MySQL, là ça peut se compliquer ... enfin pas vraiment : http://us4.php.net/manual-lookup.php?pattern=escape_string&lang(...)

      On peut très bien se baser sur le type de base de données utilisé pour apeller la bonne fonction :



      define ("dbType", "MySQL");
      $escapeStringFunction = dbType . "_escape_string";
      $securedQuery = $escapeStringFunction ($unSecuredQuery);



      J'ai fait une version "feignasse", mais dans la pratique, on pourrait tester si le type de base est MySQL et utiliser mysql_real_escape_string dans ce cas, sinon utiliser dbType . "_escape_string".
  • # Réaction rapide

    Posté par  (site web personnel) . Évalué à 3.

    Si l'exemple 27-4 te fait peur, j'espère que ce n'est pas parce que tu stockes tes mots de passe en clair dans la base de donnée! Si c'est le cas, c'est très mal! Stocke plutôt une empreinte du mot de passe générée à l'aide de la fonction md5(string).
    Ensuite, arrange toi pour limiter au maximum les droits de l'utilisateur qu'utilise ton site pour accéder à la base de données.

    Maintenant pour le reste, personnellement, je m'en tiens aux addslashes et à bien encadrer chaque valeur de variable de quotes.
    • [^] # Re: Réaction rapide

      Posté par  . Évalué à 1.

      Non, non, j'ai toujours crypté les mots de passe en md5 avant de les stocker! Seule chose qui me fait peur, c'est qu'avec la chaine md5 et aucune pitié pour son processeur, on peut revenir sur le mot de passe d'origine.
      • [^] # Re: Réaction rapide

        Posté par  . Évalué à 4.

        Autant pour moi, je me réponds à moi-même pour signaler que la technique du "grain de sel" permet de remédier à ce problème :


        define ("NoReturn", "123aze40");
        $md5Password = md5 ($_GET['password'] . NoReturn) ;

        suite du traitement ....

  • # Une fonction multi-SGBD et normalement "tout terrain"

    Posté par  . Évalué à 2.


    function coreSecureString ($stringValue) {
    //Si jamais les magic_quotes_gpc sont activés, on vire les anti-slash générés automatiquement
    if (get_magic_quotes_gpc()){
    $stringValue = stripslashes ($stringValue);
    }

    //On "échappe" la chaîne de caractère en fonction du type de base de données
    //On part du principe que DBType a déjà été déclaré de la façon suivante : define ("DBType", mysql) par exemple.

    //mysql_escape_string () étant dépréciée, on privilégie _real_escape_string ()

    if (function_exists(DBType . "_real_escape_string"))
    $escapeStringFunction = DBType . "_real_escape_string";
    else if (function_exists(DBType . "_escape_string"))
    $escapeStringFunction = DBType . "_escape_string";
    else
    //Cas ou la fonction n'existe pas pour ce SGBD
    $escapeStringFunction = "addslashes";

    //On renvoie la chaine sécurisée
    return $escapeStringFunction ($stringValue);
    }

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.