Injection SQL

      Une question m’a été posée : les instructions préparées (prepared statements) protègent-elles des failles de sécurité ?
      La question concernait les injections SQL et la réponse courte est NON, cela n’a rien à voir. Les injections SQL sont des failles de sécurité qui surviennent lorsque nous donnons la possibilité à un utilisateur d’ajouter son code SQL au nôtre.
      Illustration :

select version(); version --------------------------------------------------------------------------------------------------------------------------- PostgreSQL 10.1 on x86_64-pc-linux-gnu (Debian 10.1-2.pgdg90+1), compiled by gcc (Debian 6.3.0-18) 6.3.0 20170516, 64-bit (1 ligne) create table comptes(login text primary key, mdp text); CREATE TABLE insert into comptes values('user01', 'mdp01'); INSERT 0 1 Code php : <?php

if(!empty($_POST['login']) && !empty($_POST['mdp']))
{
try {
   
$dbconn=new PDO('pgsql:host=xxxx;port=xxxx;dbname=xxxx;user=xxxx;password=xxxx');   
} catch (
PDOException $e) {
    echo 
'Échec lors de la connexion : ' $e->getMessage();
}
$login $_POST['login'];
$mdp $_POST['mdp'];
$row $dbconn->query("select 'OUI' as ok from comptes where login='$login' and mdp='$mdp'")->fetch(PDO::FETCH_OBJ);
if(!empty(
$row))
{
echo 
$row->ok;
}
else
{
echo 
'NON';
}
}
else

echo 
'<form action="cerbere.php" method="post">';
echo 
'<p>Login : <input type="text" name="login" /></p>';
echo 
'<p>Mot de passe : <input type="text" name="mdp" /></p>';
echo 
'<p><input type="submit" value="OK"></p>';
echo 
'</form>';
}
?>
Essai avec login : user01 et mot de passe : mdp01, résultat OUI Essai avec login : user01 et mot de passe : mdp02, résultat NON Essai avec login : user01' or '1'='1 et mot de passe : mdp02' or '1'='1, résultat OUI !!!!!!!! Extrait du log postgresql : 2018-01-10 18:05:15.104 CET [18575] postgres@postgres LOG: exécute pdo_stmt_00000001: select 'OUI' as ok from comptes where login='user01' or '1'='1' and mdp='mdp02' or '1'='1' 2018-01-10 18:05:15.105 CET [18575] postgres@postgres LOG: instruction : DEALLOCATE pdo_stmt_00000001

      Nous avons simplement collecté les informations renseignées par l’utilisateur avant de les stocker dans des variables php et de les utiliser pour constituer le code SQL de la requête. Le log postgresql montre que l’instruction a été préparée mais nous avons malgré tout été victimes d’une injection SQL.
      Nous allons tenter de modifier le code php pour corriger la faille :

Code php : <?php

if(!empty($_POST['login']) && !empty($_POST['mdp']))
{
try {
   
$dbconn=new PDO('pgsql:host=xxxx;port=xxxx;dbname=xxxx;user=xxxx;password=xxxx');   
} catch (
PDOException $e) {
    echo 
'Échec lors de la connexion : ' $e->getMessage();
}
$login $_POST['login'];
$mdp $_POST['mdp'];

$sel "SELECT 'OUI' as ok from comptes where login=:login and mdp=:mdp";
$req $dbconn->prepare($sel);
$req->bindParam(':login'$login,  PDO::PARAM_STR);
$req->bindParam(':mdp'$mdp,  PDO::PARAM_STR);
$req->execute();
$row $req->fetch();

if(!empty(
$row))
{
echo 
$row[0];
}
else
{
echo 
'NON';
}
}
else

echo 
'<form action="cerbere.php" method="post">';
echo 
'<p>Login : <input type="text" name="login" /></p>';
echo 
'<p>Mot de passe : <input type="text" name="mdp" /></p>';
echo 
'<p><input type="submit" value="OK"></p>';
echo 
'</form>';
}
?>
Essai avec login : user01 et mot de passe : mdp01, résultat OUI Essai avec login : user01 et mot de passe : mdp02, résultat NON Essai avec login : user01' or '1'='1 et mot de passe : mdp02' or '1'='1, résultat NON Extrait du log postgresql : 2018-01-10 18:38:32.292 CET [18746] postgres@postgres LOG: exécute pdo_stmt_00000001: SELECT 'OUI' as ok from comptes where login=$1 and mdp=$2 2018-01-10 18:38:32.292 CET [18746] postgres@postgres DéTAIL: paramètres : $1 = 'user01'' or ''1''=''1', $2 = 'mdp02'' or ''1''=''1' 2018-01-10 18:38:32.293 CET [18746] postgres@postgres LOG: instruction : DEALLOCATE pdo_stmt_00000001


      Les informations saisies par l’utilisateur ont cette fois uniquement été utilisées pour renseigner des bind variables et non pas pour construire le code de la requête, ce qui empêche un attaquant d’ajouter son code SQL au nôtre.

Mise à jour : 11/01/2018