Qui peut m'expliquer un peu ce code qui... fonctionne bien

WRInaute occasionnel
Bonjour,

J'ai un petit bout de code là, qui remplit bien son office.
Mais je ne pige pas bien les différentes étapes. Si quelqu'un veut bien les détailler en bon français, je l'en remercie par avance :

Code:
$sql = "INSERT INTO pmot_clef (libelle,libelle_url) VALUES ('$libelle','$libelle_url')";
$stmt = $dbh->prepare ($sql); /* On prépare la requête */
$stmt->execute();

//on récupère l'id du tuple qui vient d'être créé pour incrémenter le compteur
$nouveau_tuple=$dbh->LastInsertId();

//on incrémente le compteur du nouveau tuple créé
 $sql = "UPDATE mot_clef SET compteur=compteur+1 WHERE id=".$nouveau_tuple."";
 $stmt = $dbh->prepare ($sql); /* On prépare la requête */
 $stmt->execute();

Donc ça fonctionne. Mais précisément : pourquoi la valeur de $nouveau_tuple est-elle attribuée via $dbh et non $stmt ?
Ou autrement dit, pourquoi utilise-t'on la fonction LastInsertId() avec $dbh plutôt qu'avec $stmt ?

Merci d'avance
 
WRInaute passionné
LastInsertedID te donne directement l'Id de la dernière ligne insérée ou updatée (id étant une clé primaire autoincrement). C'est plus rapide que de préparer une requète pour retrouver la valeur max d'Id et de l'exécuter.
 
WRInaute discret
tu ne veux pas lire la doc, pourtant, c'est très utile :
http://php.net/manual/fr/pdostatement.execute.php

exemple 1 : pas de paramètre à execute, car ils ont déjà été passés
exemple 2 : les paramètres sont transmis directement à la fonction execute

dans ton code, il n'y a pas de paramètre donc execute sans rien fonctionne, sans besoin de bindparam
 
WRInaute accro
Oui il a pas envie de lire la doc qui explique ça très bien et il s'obstine à mettre des failles d'injection SQL.
 
WRInaute occasionnel
Salut à vous et merci pour vos réponses.
MikeR a dit:
LastInsertedID te donne directement l'Id de la dernière ligne insérée ou updatée (id étant une clé primaire autoincrement). C'est plus rapide que de préparer une requète pour retrouver la valeur max d'Id et de l'exécuter.
Non, ce n'est pas ma question.
Dans cette ligne précise :
Code:
$nouveau_tuple=$dbh->LastInsertId();
Pourquoi n'a-t'on pas plutôt :
Code:
$nouveau_tuple=$stmt->LastInsertId();
Ah bah si, c'est ma question : tu veux dire que la fonction LastInsertedID() est une sorte de requête pré-programmée qui n'a plus besoin que d'appeler les paramètres de la BD, pour être exécutée. Mais comment donc cette fonction peut-elle savoir quelle est la table concernée ?

Louis63 a dit:
tu ne veux pas lire la doc, pourtant, c'est très utile :
http://php.net/manual/fr/pdostatement.execute.php

exemple 1 : pas de paramètre à execute, car ils ont déjà été passés
exemple 2 : les paramètres sont transmis directement à la fonction execute

dans ton code, il n'y a pas de paramètre donc execute sans rien fonctionne, sans besoin de bindparam
Je compte ouvrir un topic sur cette fameuse doc' de manière à pouvoir la lire. Il y a des choses que je ne comprends pas dans la présentation, raison pour laquelle je ne m'en sers pas.
Exemple :
Code:
public bool PDOStatement::execute ([ array $input_parameters ] )
Je n'arrive pas à traduire cela en code opérationnel. Qu'est-ce que ce "public bool" ? (souvent on a public string). C'est une présentation formelle de la fonction. Mais je ne sais pas lire ça.

spout a dit:
Oui il a pas envie de lire la doc qui explique ça très bien et il s'obstine à mettre des failles d'injection SQL.
Fais pas dans le suspens spout, c'est pas les oiseaux. Elle est où ici, la fenêtre par laquelle entre les corbeaux ?
 
WRInaute accro
là:
PHP:
<span class="syntaxdefault">$sql&nbsp;</span><span class="syntaxkeyword">=&nbsp;</span><span class="syntaxstring">"UPDATE&nbsp;mot_clef&nbsp;SET&nbsp;compteur=compteur+1&nbsp;WHERE&nbsp;id="</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">$nouveau_tuple</span><span class="syntaxkeyword">.</span><span class="syntaxstring">""</span><span class="syntaxkeyword">;&nbsp;</span><span class="syntaxdefault"></span>
 
WRInaute occasionnel
Hum... Et l'erreur viendrait de la variable $nouveau_tuple, passée en paramètre de recherche ?
Si oui, je n'arrive pas à voir le problème, puisque la valeur de la variable ne provient pas d'une donnée transmise par _GET[] ou $_POST[]. Donc comment pourrait-elle contenir une valeur dangereuse ?
Mais en attendant d'en savoir plus et si le problème vient bien de là, j'imagine que la solution serait alors d'utiliser les requêtes préparées, avec bind_param() ? Si oui, je ne vois pas l'intérêt pour la raison ci-dessus. Ca m'a tout l'air d'alourdir le code d'une ligne de plus (mais mes compétences sont un peu boiteuses, j'en conviens).
 
WRInaute passionné
Alorsladaccord a dit:
]Mais comment donc cette fonction peut-elle savoir quelle est la table concernée ?
On se fout de la table.Il te donne la valeur du dernier id autoincrémenté dans ton contexte. Le dernier. C'est à toi de savoir ce à quoi il se rapporte.
 
WRInaute discret
Alorsladaccord a dit:
Code:
public bool PDOStatement::execute ([ array $input_parameters ] )
Je n'arrive pas à traduire cela en code opérationnel. Qu'est-ce que ce "public bool" ? (souvent on a public string). C'est une présentation formelle de la fonction. Mais je ne sais pas lire ça.
public bool ça veut dire que c'est une fonction publique (accessible à n'importe quel instance de l'objet) et qu'elle renvoit une valeur de type boolean (true ou false).
 
WRInaute occasionnel
niap a dit:
Alorsladaccord a dit:
Code:
public bool PDOStatement::execute ([ array $input_parameters ] )
Je n'arrive pas à traduire cela en code opérationnel. Qu'est-ce que ce "public bool" ? (souvent on a public string). C'est une présentation formelle de la fonction. Mais je ne sais pas lire ça.
public bool ça veut dire que c'est une fonction publique (accessible à n'importe quel instance de l'objet) et qu'elle renvoit une valeur de type boolean (true ou false).

Salut niap,

Et ce que tu nous dis-là, ce sont des classiques de l'informatique transcendantale, valable pour n'importe quel langage ? Je suis donc supposé apprendre quoi, pour comprendre le manuel PHP ? Les rigueurs théoriques de base de l'informatique, quel que soit le langage ? Genre BTS première année, un truc comme ça ?
 
WRInaute passionné
Alorsladaccord a dit:
Et ce que tu nous dis-là, ce sont des classiques de l'informatique transcendantale, valable pour n'importe quel langage ? Je suis donc supposé apprendre quoi, pour comprendre le manuel PHP ? Les rigueurs théoriques de base de l'informatique, quel que soit le langage ? Genre BTS première année, un truc comme ça ?
Oui, c'est le b-a-ba de la programmation objet qui est mise oeuvre dans pas mal de langages (php, java, c++). Il y a de nombreux sites d'initiation là dessus (et certains utilisant php comme example).
 
WRInaute accro
spout a dit:
là:
PHP:
<span class="syntaxdefault">$sql </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">"UPDATE mot_clef SET compteur=compteur+1 WHERE id="</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">$nouveau_tuple</span><span class="syntaxkeyword">.</span><span class="syntaxstring">""</span><span class="syntaxkeyword">;&nbsp;</span><span class="syntaxdefault"></span>

En quoi s'agit-il d'une réelle faille dans la mesure ou l'on sait d'ou vient $nouveau_tuple. Ta remarque m'intéresse même si de mon côté j'ai tendance a utiliser cette forme pour les requêtes préparées :

"
$result = bdd->prepare(UPDATE mot_clef SET compteur=compteur+1 WHERE id= ?)
$result->execute($params);
";

ou alors id=:id, combiné avec bindvalue
 
WRInaute accro
PHP:
<span class="syntaxdefault">$nouveau_tuple </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> $_GET</span><span class="syntaxkeyword">[</span><span class="syntaxstring">'nouveau_tuple'</span><span class="syntaxkeyword">];</span><span class="syntaxdefault"></span>
?nouveau_tuple=2;DELETE FROM membres--
 
WRInaute accro
Un truc m'échappe surement, mais dans son code $nouveau_tuple n'est pas récupéré a partir d'un get :

Code:
$nouveau_tuple=$dbh->LastInsertId();
 
WRInaute occasionnel
spout a dit:
PHP:
<span class="syntaxdefault">$nouveau_tuple </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> $_GET</span><span class="syntaxkeyword">[</span><span class="syntaxstring">'nouveau_tuple'</span><span class="syntaxkeyword">];&nbsp;</span><span class="syntaxdefault"></span>
?nouveau_tuple=2;DELETE FROM membres--
Oui, mais dans ce cas-là, j'applique une fonction PHP sanitize int sur le tuple récupéré par _GET ou _POST.

Par contre, j'ai trouvé une erreur bizarre, qui ne se produit qu'en localhost.
Voilà : si je fais cela sur mon serveur (OVH mais peu importe je présume) :
Code:
$nouveau_tuple = filter_var($_GET['nouveau_tuple', FILTER_SANITIZE_NUMBER_INT));
Alors, tout fonctionne bien.

Par contre, si je travaille en local (version PHP plus ancienne sans nul doute)
Alors ça me déclenche une erreur me disant que la variable n'est pas initialisée.
Il faut alors que je fasse comme ça
Code:
if(isset($_GET['nouveau_tuple'])) {$nouveau_tuple = filter_var($_GET['nouveau_tuple', FILTER_SANITIZE_NUMBER_INT)); }

La question que je me pose est :
- Faut-il obligatoirement faire de même sur le code en production (sur le serveur OVH) ?
- Et pourquoi n'y-a-t'il pas la même erreur sur celui-ci ?
 
Nouveau WRInaute
Bonjour,

1/
$dbh est une référence (#ressource) à la base de données, et c'est la base de données qui gère ce genre d'infos. Pourquoi ? Parce que le système en question est fait comme ça :)

2/
Si tu travailles en local, alors tu vois l'erreur, alors que si tu travailles en distant, tu ne la vois pas. Mais rien ne dit qu'elle n'est pas là !
Au début de ton code, tu peux mettre :
ini_set('display_errors',1);
error_reporting(E_ALL);

Devrait suffire à t'afficher toutes les erreurs.

Ceci dit :
Que se passe t-il si la variable n'est pas instanciée ? Ca affiche un warning, ok, et what ??
Comme tu dis, si la valeur existe, tu fais un sanitize, mais si elle n'existe pas, tu as donc une faille de sécurité ;)
 
WRInaute occasionnel
Salut Anonymus, merci pour ta participation.

Ta dernière phrase confirme ma crainte. Là je termine le "gros œuvre", ensuite je verrai pour les questions de sécurité.

Mais en deux mots déjà, histoire de faire avancer un peu le truc. En quoi le fait d'appliquer une fonction sanitize sur une fonction non instanciée génère-t'il une faille de sécurité ? Une variable non instanciée diffère d'une variable avec une valeur nulle, certes, mais pourquoi la fonction sanitize (ou une autre) ne fait-elle pas tout simplement fi de l'opération ?

Il ne me semble pas qu'existait quelques années auparavant, ce problème d'instanciation obligatoire des variables. Je me trompe ?
 
Nouveau WRInaute
En php, rien n'est obligatoire.. ou pas grand chose.
Le problème étant que, si tu ne t'occupes pas toi même de la valeur d'une variable, alors tu délègues cette assignation au système.
Quelle valeur va t-il lui attribuer ??

La question a toujours existé, mais avant 'on' pensait que ca n'était pas grave. Les hackeurs ont prouvé que... ben si.

Donc :
Le problème est que tu donnes des bouts de code, au fur et à mesure des réponses de chacun. Ce qui fait que personne ne sait trop comment est "l'ensemble" du code.
Ceci dit, si je me réfère au dernier morceau, j'ai ceci :

Code:
if(isset($_GET['nouveau_tuple'])) {$nouveau_tuple = filter_var($_GET['nouveau_tuple', FILTER_SANITIZE_NUMBER_INT)); }
Que je peux traduire par

"si la variable $_GET['nouveau_tuple'] existe, alors $nouveau_tuple .... "

ce qui me fait penser à : " et si elle n'existe pas ?"

De là, 2 solutions :
Soit : $nouveau_tuple est false, null, comme tu veux..
Soit : tu as un truc qui s'appelle 'register_globals' qui peut (potentiellement) lui attribuer la valeur : $_POST['nouveau_tuple'] ou $_COOKIES['nouveau_tuple'], ...
Et donc, on se trouve avec ce qui disait 'spout', à savoir :

Code:
nouveau_tuple=2;DELETE FROM membres

qui ira instancier la variable 'nouveau_tuple'.

Tu vas me dire 'ouaip, mais 'register_globals' est à 'off'.
Ok, et combien de temps ? Tu verifies tous les matins ? Tu changes jamais de version d'OS, de php, de... ?

Tu peux garantir, tout simplement ce morceau de code, en mettant par exemple :
Code:
if(isset($_GET['nouveau_tuple'])) {$nouveau_tuple = filter_var($_GET['nouveau_tuple', FILTER_SANITIZE_NUMBER_INT)); }
else{$nouveau_tuple =0;}
ou
Code:
$nouveau_tuple =0;
if(isset($_GET['nouveau_tuple'])) {
$nouveau_tuple = filter_var($_GET['nouveau_tuple', FILTER_SANITIZE_NUMBER_INT)); 
}

Tu devrais aussi, puisque tu utilises 'pdo', te servir des protections de celui-ci. Après tout, il a été fait pour ca aussi.
Ainsi, ton code :
Code:
 $sql = "UPDATE mot_clef SET compteur=compteur+1 WHERE id=".$nouveau_tuple."";
 $stmt = $dbh->prepare ($sql); /* On prépare la requête */
devrait être écrit
Code:
 $sql = "UPDATE mot_clef SET compteur=compteur+1 WHERE id=:nouveau_tuple";
 $stmt = $dbh->prepare ($sql); /* On prépare la requête */
 $stmt->bindParam(':nouveau_tuple', $nouveau_tuple, PDO::PARAM_INT);
Ainsi, donc, tu demandes explicitement à pdo de n'accepter que des valeurs 'integer'.

Ca m'a tout l'air d'alourdir le code d'une ligne de plus (mais mes compétences sont un peu boiteuses, j'en conviens).

En fait, quand tu te réveilles un beau matin et que ton répertoire est 'vide', que ton code a 'disparu' ben tu te dis que la prochaine fois tu compteras pas les lignes ;)


a+ !
 
WRInaute occasionnel
Salut cher Anonymus et merci beaucoup pour ta réponse très complète. J'embraye avec quelques questions supplémentaires, pour préciser un peu la chose, si tu veux bien.

Anonymus a dit:
Code:
if(isset($_GET['nouveau_tuple'])) {$nouveau_tuple = filter_var($_GET['nouveau_tuple', FILTER_SANITIZE_NUMBER_INT)); }
Que je peux traduire par :
"si la variable $_GET['nouveau_tuple'] existe, alors $nouveau_tuple .... "
ce qui me fait penser à : " et si elle n'existe pas ?"
Eh bien, je ne comprends pas bien ce point.
Par exemple, si je fais cela :
Code:
$nouveau_tuple = filter_var($_GET['nouveau_tuple', FILTER_SANITIZE_NUMBER_INT));
Alors j'ai une erreur PHP en local. Cette erreur, je la supprime en ajoutant la condition if ci-dessus.
Partant de là, je ne comprends pas ce que je risque : si la condition if n'est pas vérifiée et bien c'est qu'il n'y a pas de variable $_GET. Donc il ne se passe rien. Et s'il ne se passe rien, un pirate ne peut rien faire avec le rien. Me dis-je.


Anonymus a dit:
Tu vas me dire 'ouaip, mais 'register_globals' est à 'off'.
Ok, et combien de temps ? Tu verifies tous les matins ? Tu changes jamais de version d'OS, de php, de... ?
C'est vrai, je ne suis pas allé chercher jusque là. Ceci dit, ça vaut aussi pour le hacker : va-t'il vérifier chaque matin si le register_globals de mon site est à off ? Enfin en théorie, tu as surement raison.

Code:
if(isset($_GET['nouveau_tuple'])) {$nouveau_tuple = filter_var($_GET['nouveau_tuple', FILTER_SANITIZE_NUMBER_INT)); }
else{$nouveau_tuple =0;}
Oui, mais ça ne me plait pas : parce que maintenant, la variable $nouveau_tuple est créée et cela peut générer l'exécution d'autres conditions par la suite, alors qu'en fait, elle n'est avait pas été réceptionnée par l'URL.

Anonymus a dit:
Tu devrais aussi, puisque tu utilises 'pdo', te servir des protections de celui-ci. Après tout, il a été fait pour ca aussi.
Ainsi, ton code :
Code:
 $sql = "UPDATE mot_clef SET compteur=compteur+1 WHERE id=".$nouveau_tuple."";
 $stmt = $dbh->prepare ($sql); /* On prépare la requête */
devrait être écrit
Code:
 $sql = "UPDATE mot_clef SET compteur=compteur+1 WHERE id=:nouveau_tuple";
 $stmt = $dbh->prepare ($sql); /* On prépare la requête */
 $stmt->bindParam(':nouveau_tuple', $nouveau_tuple, PDO::PARAM_INT);
Ainsi, donc, tu demandes explicitement à pdo de n'accepter que des valeurs 'integer'.
Ok, ça je vais le faire à la fin, quand je repasserai sur la sécurité du site, point par point. Deux précautions valent mieux qu'une, j'en conviens.


Encore merci pour ta réponse, Anonymus. Une reco, une !
 
Discussions similaires
Haut