Protection d'une base de donnée

Vous avez un soucis de développement et ce n'est pas du ressort de notre support ? N'hésitez pas à soumettre vos petits soucis de développement et à vous entre aider par la même occasion.
Starscot
Messages : 4
Inscription : 5 août 2013 à 11:57

Bonjour,

J'ai construit un formulaire d'inscription pour mon site, voulant le rendre plus sécurisé, j'ai décidé d'utiliser cette fonction pour protéger les entrée dans la table users :

Code : Tout sélectionner

$str = mysqli_real_escape_string($str);
Tout semble fonctionner correctement car avant inscription ma page vérifie que le pseudo du joueur n'est pas déjà inscrit et celà affiche bien l'erreur quand le compte existe (vérification par pseudo).
Cependant lorsque je vais sur phpMyAdmin, les entrées protégées avec cette fonction apparaissent comme vide (ex. pseudo) Est-ce normal ou faut-il protéger autrement ?

Image


Je vous remercie d'avance.
Avatar de l’utilisateur
Elodie
Fondatrice / Responsable
Fondatrice / Responsable
Messages : 7938
Inscription : 2 avril 2010 à 20:14

Bonjour,

La fonction PHP mysqli_real_escape_string() permet d'échapper les apostrophes et autres signes pouvant induire en erreur un serveur MySQL à exécuter sans l'avoir désiré, une sous requête MySQL. On nomme ce type d'attaque : une injection SQL.

Le principe est d'introduire une requête à l'intérieur d'une autre requête afin soit de supprimer des données MySQL ou d'obtenir une réponse toujours vrai quelque soit les argument suivants, cette réponse vrai est souvent utilisé dans les authentifications.

Cependant, bien que la fonction mysqli_real_escape_string() soit primordiale dans les anciens développements, elle a rapidement été remplacée par une autre méthode de développement, que l'on nomme PDO.

Le principe du PDO est de construire une requête de manière sécurisé et indépendante du moteur de base de données SQL utilisé. Le second point est que l'on vas encapsuler une suite de requêtes MySQL et effectuer ou non à la fin, un commit afin d'exécuter ou pas le traitement qui a été fait. Ainsi, s'il y a un problème pendant l'exécution, les services vont "revenir en arrière" (rollback) comme si rien n'avait été fait.

Grosso modo, dans le principe de base "en français" ça fonctionne ainsi :

Code : Tout sélectionner

ligne_sql = preparation("INSERT INTO table (nom, age) VALUES ( :nom, :age);
construction( ligne_sql, tableau(:nom => $nom, :age => $age))
execution(construction)
Voilà un exemple de code avec une fonction perso que j'utilise afin de simplifier l'écriture :
function perso_pdo_query($query, $data='')
{
global $pdo;
if(empty($data))
{
$sth = $pdo->query($query);
}else{
$data = unstrip_array($data);
$sth = $pdo->prepare($query);
$sth->execute($data);
}
return $sth;
}

$sql= "INSERT INTO table (nom, age) VALUES ( :nom, :age);";
perso_pdo_query($sql, array(':nom' => $nom, ':age' => $age));
Ainsi, je n'ai plus besoin de me soucier si âge est un nombre ou non, inutile d'encadrer les valeurs. De plus, si la personne met dans son âge une requête MySQL, la préparation et la construction de la requête n'interprétera pas la valeur comme une sous requête injectée. Le serveur PHP/MySQL ne se font ainsi plus induire en erreur.

La connexion MySQL a préalablement été effectuée en PDO et placée dans l’objet de nom $pdo que je réutilise dans la fonction en l'important depuis le global.

Voilà un code de connexion en PDO :

Code : Tout sélectionner

function perso_pdo_connect()
  {
  global $pdo;
  try
    {
    $pdo = new PDO('mysql:host=<host>;dbname=<base>', '<user>', '<password>',
               array(PDO::ATTR_PERSISTENT => true));
    }
    catch(Exception $e)
    {
    echo("Erreur MySQL n° ".$e->getCode()." : ".$e->getMessage());
    exit();
    }
  }
Pour démarrer une transaction MySQL avec PDO, voilà un autre code :

Code : Tout sélectionner

  try
    {
    $pdo->beginTransaction();

    // le code mysql ici

    $pdo->commit();

    }catch (PDOException $e){
    //sinon on retourne l'erreur MySQL à l'admin du site : $e
    }
  }
Voilà grossièrement :)
starscot a écrit :les entrées protégées avec cette fonction apparaissent comme vide (ex. pseudo) Est-ce normal ou faut-il protéger autrement ?
Non ce n'est pas normal, vous devriez toujours pouvoir accéder aux informations que vous enregistrés :)

Aussi, je déplace votre question dans la partie développement car ça ne concerne pas directement le support technique ;)
Vous avez une question ? Posez-la de préférence sur le forum et si ça demande un contact plus instantané, n'hésitez pas à vous rendre sur le t'chat IRC. Si votre question est personnelle, contactez-nous directement.
Starscot
Messages : 4
Inscription : 5 août 2013 à 11:57

Ah d'accord je ne connaissais pas cette méthode d'annuler en cas d'erreur.
Cela suffit-il pour contrer les injections sql susceptibles d’être tenté en utilisant un champ de formulaire ?

Je vous remercie pour votre aide car j’avoue que cela fait un moment que je bloque dessus.
Avatar de l’utilisateur
Elodie
Fondatrice / Responsable
Fondatrice / Responsable
Messages : 7938
Inscription : 2 avril 2010 à 20:14

starscot a écrit :Ah d'accord je ne connaissais pas cette méthode d'annuler en cas d'erreur.
Cela suffit-il pour contrer les injections sql susceptibles d’être tenté en utilisant un champ de formulaire ?
Non et ça n'a rien à voir.

Un "DROP TABLE" par exemple ne peut pas être annulé, puis lors d'une vérification d'authentification (un SELECT), la transaction sera totalement inutile car c'est prévu notamment pour les UPDATE/DELETE.

Il ne faut pas confondre sécurité de l'intégrité des données (les transactions) avec la pérennité des données (protection anti-injection). Les transactions sont là pour garantir que les données restent cohérentes, pas pour les protéger d'une faille.
Vous avez une question ? Posez-la de préférence sur le forum et si ça demande un contact plus instantané, n'hésitez pas à vous rendre sur le t'chat IRC. Si votre question est personnelle, contactez-nous directement.
Répondre