Bonjour à tous

Débutant en PHP-Mysql, je souhaiterai savoir si cette page de validation des membres par un admin est "injectable" si oui pourquoi et comment corriger mes erreurs. Cette page est bien entendu protégée par un Htaccess réservée à l'admin.
En fait, je souhaiterai connaitre certaines bases pour "coder" de manière sécurisée à chaque fois que je crée quelque-chose afin d'avoir certains réflexes.

Merci à ceux qui prendrons le temps de m'expliquer tout cela.

la bdd :

<?php
$pdo = new PDO('mysql:dbname=test;host=localhost;charset=UTF8', 'root', '');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);


ma page :


<?php
require 'bdd.php';
$req = $pdo->query('SELECT * FROM validation');
while ($validation = $req->fetch())
{
echo 'Nom : ';
echo $validation->nom;
echo ' Prénom: ';
echo $validation->prenom;
echo ' E-mail : ';
echo $validation->email;
echo '<a href="validation.php?action=accepter&id='.$validation->id.'">Accepter</a>';
echo '<a href="validation.php?action=refuser&id='.$validation->id.'">Refuser</a>';
echo '<br/>';
}
 
if(isset($_GET['action']) AND isset($_GET['id']))
{
$action = $_GET['action'];
if($action == "accepter")
{
$id = $_GET['id'];
$req2 = $pdo->query("SELECT * FROM validation WHERE id='$id'");
$connexion = $req2->fetch();
$nom = $connexion->nom;
$prenom = $connexion->prenom;
$email = $connexion->email;
$pass = $connexion->pass;
$pdo->query("INSERT INTO utilisateurs VALUES('$id', '$nom', '$prenom', '$email', '$pass')");
$pdo->query("DELETE FROM validation WHERE id='$id'");
}
elseif($action == "refuser")
{
$id = $_GET['id'];
$pdo->query("DELETE FROM validation WHERE id='$id'");
}
}
?>
Bonjour.

Oui elle l'est. Cependant si la page est protégée par un autre moyen, seuls ceux y ayant accès pourront faire de l'injection. Toutefois il faut la corriger, car il faut 1) prendre des bons reflexes, 2) même l'admin ne doit pas «outrepasser ses droits» par l'injection.

L'injection consiste à bricoler la requête grâce à des données passées en GET ou en POST et qui sont utilisées pour construire les requêtes.

Pour s'en protéger, il ne faut jamais construire des requêtes à partir de variables contenant des données insérées par l'utilisateur, sauf si elles ont été préalablement nettoyées.
Avec l'extension msqli, on peut ainsi nettoyer les données:

$id = mysqli_real_escape_string($_GET['id']);

Cela va échapper certains caractères permettant de bricoler les requêtes. Cependant ce n'est pas parfait et il faut bien savoir ce que l'on fait.

Une méthode plus moderne et robuste consiste à utiliser les requêtes préparées. Une requête préparée permet de créer une requête avec des paramètres, que l'on va ensuite pouvoir appeler plusieurs fois avec des valeurs de paramètres différents:
SELECT * FROM validation WHERE id = ?

On va pouvoir appeler ainsi cette requêtes plusieurs fois avec des valeurs d'id différents.
Même si on n'a pas besoin d'appeler une requête plusieurs fois, le gros avantage de cette méthode est le suivant: Les paramètres passés à la requête préparée ne sont pas analysés par le moteur de base de donnée comme du code SQL. Cela empêche donc de pouvoir «bricoler» la requête.

en PDO cela se passe ainsi:


$sth = $dbh->prepare('SELECT * FROM validation WHERE id = :id AND truc = :truc');

$res = $sth->execute(array(':id' => 12, ':truc' => 'machin'));

$row = $res->fetch();


Une requête préparée peut l'être avec des paramètres nommés (comme ici) ou avec des marqueurs «?»
Les paramètres peuvent être passés lors de l'éxecution comme ici, un par un avec la méthode bindParam, ou en groupe avec la méthode bindParams.
Je pense retenir la méthode des requête préparées, cependant j'ai du mal à comprendre le cheminement dans le déroulé des requêtes et surtout récupérer les infos à manipuler dans la BDD...

exemple : j'ai modifié la première partie avec cette méthode (corrige moi si j'ai faux !) en attendant ça fonctionne.

<?php
require_once 'db.php';
$req = $pdo->prepare('SELECT * FROM validation');
$req->execute();
while ($validation = $req->fetch())
{
echo 'Nom : ';
echo $validation->nom;
echo ' Prénom: ';
echo $validation->prenom;
echo ' E-mail : ';
echo $validation->email;
echo '<a href="validation.php?action=accepter&id='.$validation->id.'">Accepter</a>';
echo '<a href="validation.php?action=refuser&id='.$validation->id.'">Refuser</a>';
echo '<br/>';
}


Par contre la 2ème partie me pose problème...

if(isset($_GET['action']) AND isset($_GET['id']))
{
$action = $_GET['action'];
if($action == "accepter")
{
$id = $_GET['id'];
$req = $pdo->prepare("INSERT INTO utilisateurs SET nom = ?, prenom = ?, email = ?, pass = ?, id = ?")->execute([$validation['nom'], $validation['prenom'], $validation['email'], $validation['pass'], $id]);

Modifié par Gianni54 (19 Nov 2017 - 19:46)
Déjà je pense avoir compris quelques erreurs...

J'appel $validation alors qu'il est "fermé" plus haut donc ne renvoi rien...
Il me faut donc recharger tout cela dans une nouvelle requête...
Voici le nouveau code qui fonctionne très bien mais ne doit pas être bien exploité !

if(isset($_GET['action']) AND isset($_GET['id']))
{
$action = $_GET['action'];
if($action == "accepter")
{
$id = $_GET['id'];
$req = $pdo->prepare('SELECT * FROM validation');
$req->execute();
$connexion = $req->fetch();
$nom = $connexion->nom;
$prenom = $connexion->prenom;
$email = $connexion->email;
$pass = $connexion->pass;
var_dump($nom);
$req2 = $pdo->prepare("INSERT INTO utilisateurs SET nom = ?, prenom = ?, email = ?, pass = ?, id = ?")->execute([$nom, $prenom, $email, $pass, $id]);
Bonjour,

Je rajouterais à ce qu'a dit kustolovic :
Tous les valeurs que tu récupères doivent être validées :
- par exemple pour $id, tu devrais vérifier que c'est bien un interger et > 0.
- si par exemple tu récupères un email, tu dois vérifier son format xxx@xxx.xx
- si par exemple tu récupères un prénom, tu dois vérifier son format si par exemple il y a un < ou un } il y a de forte chance que cela soit pas un prénom.

D'autre part, il me semble que si j'arrive a changer l'ID_ID dans :
validation.php?action=accepter&id=ID_ID"

Il me semble que cela, va pas faire ce que l'on attendait pour l'ID_ID initial.