10
votes

Remplacement des fonctions MySQL_ * avec des déclarations PDO et préparées

J'ai toujours fait la connexion simple de mysql_connect code>, mysql_pconnect code>: xxx pré>

en utilisant cela, j'ai toujours utilisé le méthode simple pour échapper aux données avant de faire une requête, que ce soit insérer code>, Sélectionnez code>, met à jour code> ou Supprimer code> par Utilisation de mysql_real_escapape_string code> p> xxx pré>

maintenant je comprends que cela est sûr, dans une mesure! p>

Il échappe aux caractères dangereux; Cependant, il est toujours vulnérable à d'autres attaques pouvant contenir des caractères sûrs, mais peut être nocive pour afficher des données ou dans certains cas, modifier ou supprimer des données malicieusement. P>

Alors, j'ai cherché un peu et a découvert À propos de PDO, MySQLI et des déclarations préparées. Oui, je suis peut-être en retard au jeu, mais j'ai lu beaucoup, beaucoup de tutoriels (Tizag, W3C, blogs, Google Recherches) et pas un seul a mentionné cela. Il semble très étrange de savoir pourquoi, comme il suffit d'échapper à l'entrée de l'utilisateur n'est vraiment pas sécurisé et pas une bonne pratique pour le moins. Oui, je suis conscient que vous puissiez utiliser regex pour l'aborder, mais je suis toujours sûr que ça ne suffit pas? P>

C'est à ma compréhension que l'utilisation de déclarations PDO / préparées est une façon beaucoup plus sûre de Stockez et récupérez des données d'une base de données lorsque les variables sont données par l'entrée de l'utilisateur. Le seul problème est que le commutateur sur (surtout après avoir été très bloqué dans mes habitudes / habitudes de codage précédent) est un peu difficile. P>

Je comprends maintenant que de vous connecter à ma base de données à l'aide de PDO, j'utiliserais P>

 $username = $_POST['username'];
 $email = $_POST['email'];

 $stmt = $dbh->prepare("INSERT INTO `users` (username, email)
                        VALUES (:username, :email)");

 $stmt->bindParam(':username, $username, PDO::PARAM_STR, ?_LENGTH_?);
 $stmt->bindParam(':email, $email, PDO::PARAM_STR, ?_LENGTH_?);

$stmt->execute();


1 commentaires

Peut-être la longueur des champs "nom d'utilisateur" et "e-mail"? Par exemple. Si le nom d'utilisateur est Varchar (32), le paramètre de longueur doit être 32.


4 Réponses :


8
votes

Je ne me préoccupe jamais avec BindParam () ou des types de paramètres ou de longueurs.

Je viens de passer une gamme de valeurs de paramètre pour exécuter (), comme ceci: xxx

Ceci est tout aussi efficace et plus facile à code. < P> Vous pouvez également être intéressé par ma présentation Mythes d'injection SQL et Fallacies , ou mon Réservez SQL AntiPatterns: Éviter les pièges de la programmation de la base de données .


2 commentaires

Pourrait-il aussi être fait avec? Les espaces réservés dans le tableau alors? Dites Sélectionnez * à partir des utilisateurs où nom =? Et email =? "); avec exécuté (tableau $ (nom $, $ email)); ou cela ne fonctionne pas dans ce cas?


Oui, vous pouvez utiliser des paramètres de position au lieu de paramètres nommés, comme vous le souhaitez.



2
votes

Pour répondre à la question de la longueur, spécifiez-le est facultatif, sauf si le paramètre de paramètre de paramètre est un paramètre OUT à partir d'une procédure stockée, donc dans la plupart des cas, vous pouvez l'omettre en toute sécurité.

En ce qui concerne la sécurité, l'échappement est terminé. dans les coulisses lorsque vous liez les paramètres. Cela est possible car vous avez dû créer une connexion de base de données lorsque vous avez créé l'objet. Vous êtes également protégé des attaques d'injection SQL depuis la préparation de la déclaration, vous indiquez à votre base de données au format de l'instruction avant que l'entrée de l'utilisateur ne puisse obtenir n'importe où. Un exemple: xxx

Ainsi, en termes de sécurité, vos exemples ci-dessus semblent bien.

Enfin, je conviens que les paramètres de liaison individuellement sont fastidieux et sont Tout comme efficacement fait avec un tableau transmis à PDOStatement-> Exécuter () (voir http: //www.php.net/manual/fr/pdostatement.execute.php ).


0 commentaires

12
votes

Merci pour la question intéressante. Vous y allez:

Il échappe aux caractères dangereux,

Votre concept est tout à fait faux.

En fait, "personnages dangereux" est un mythe, il n'y en a pas. Et mysql_real_escape_string s'échappant mais simplement un délimiteurs de chaîne . À partir de cette définition, vous pouvez conclure ses limitations - cela ne fonctionne que pour Strings .

Cependant, il est toujours vulnérable à d'autres attaques pouvant contenir des caractères sûrs, mais peut être nocif pour afficher des données ou dans certains cas, modifier ou supprimer des données malicieusement.

Vous mélangez ici tout.
Parlant de base de données,

  • pour les cordes, il est pas vulnérable. aussi longtemps que vos cordes citées et échappées, elles ne peuvent "modifier ou supprimer des données malicieusement". * < / code>
  • pour les autres données Typedata - Oui, c'est inutile . Mais pas parce que c'est un peu "dangereux" mais juste à cause d'une utilisation inappropriée.

    Quant aux données d'affichage, je suppose qu'il est offtopic dans la question liée à la PDO, comme PDO n'a rien à voir avec l'affichage des données non plus. < / p>

    Échapper l'entrée utilisateur

    ^^^ Une autre illusion à noter!

    • Une entrée d'utilisateur n'a absolument rien à voir avec une échappée . Comme vous pouvez apprendre de la définition antérieure, vous devez échapper aux chaînes, pas n'importe quelle "entrée utilisateur". Donc, encore une fois:

      • Vous avez des cordes d'évasion, quelle que soit leur source
      • Il est inutile d'échapper à d'autres types de données, quelle que soit la source.

        a eu le point?
        Maintenant, j'espère que vous comprenez les limites de l'échappement ainsi que de la fausse idée des "personnages dangereux".

        C'est à ma compréhension que l'utilisation de déclarations de PDO / préparées est une beaucoup plus sûre

        pas vraiment.
        En fait, il existe quatre différentes parties de requête que nous pouvons y ajouter de manière dynamique:

        • une chaîne
        • un nombre
        • un identifiant
        • Un mot clé de syntaxe.

          Ainsi, vous pouvez voir que l'échappement ne couvre qu'un seul problème. (Mais bien sûr, si vous traitez des chiffres en tant que chaînes (les mettre dans des guillemets), le cas échéant, vous pouvez les faire en sécurité)

          tandis que des déclarations préparées - UGH - Entier 2 Isues! Une grosse affaire; -)

          pour les 2 autres numéros Voir ma réponse précédente, Dans PHP Lors de la soumission des chaînes à la base de données Devrais-je m'occuper de caractères illégaux à l'aide de HTMLSpecialchars () ou d'utiliser une expression régulière?

          MAINTENANT, les noms de fonction sont différents, donc ne plus mon mysql_query, mysql_fetch_array, mysql_num_rows, etc.

          c'est une autre, grave illusion de php utilisateurs , une catastrophe naturelle, une catastrophe:

          même lorsqu'il utilise Old MySQL Driver, On ne devrait jamais utiliser les fonctions d'API nues dans leur code! Il faut les mettre dans une fonction de bibliothèque pour l'utilisation quotidienne! (Pas comme un rite magique, mais simplement pour rendre le code plus court, moins répétitif, une épreuve d'erreur, plus cohérente et lisible).

          la même chose va aussi pour le PDO!

          maintenant sur votre question à nouveau.

          mais en les utilisant, cela élimine la nécessité d'utiliser quelque chose comme mysql_real_escapape_string?

          oui.

          Mais je pense que c'est à peu près l'idée de ce qui devrait être fait pour récupérer un utilisateur d'une base de données

          ne pas récupérer, mais pour ajouter une donnée à la requête !

          Vous devez donner une longueur après PDO: Param_str si je ne me trompe pas

          Vous pouvez, mais vous n'êtes pas obligé.

          Maintenant, est-ce tout sûr?

          En termes de sécurité de la base de données, il n'y a pas de points faibles dans ce code. Rien à sécuriser ici.

          pour l'affichage de la sécurité - il suffit de rechercher ce site pour le mot clé xss .

          J'espère que je vis une lumière sur la matière.

          BTW, pour les inserts longs, vous pouvez utiliser la fonction que j'ai écrit un jour, Insérer / mettre à jour la fonction d'assistance à l'aide de PDO

          Cependant, je n'utilise pas de déclarations préparées pour le moment, car je préfère mes espaces réservés à domicile sur eux, en utilisant une bibliothèque j'ai mentionné ci-dessus. Donc, pour contrer le code posté par le RIHA ci-dessous, il serait aussi court que ces 2 lignes: xxx

          mais bien sûr, vous pouvez avoir le même code à l'aide de déclarations préparées bien.


          * (Oui, je suis au courant des contes de peur de Schiflett)


8 commentaires

Merci :) maintenant, je suppose. Je marque généralement la question elle-même, si je veux le signet


Pourquoi encourager l'OP à utiliser une bibliothèque auto-codée lorsque l'OP demande réellement comment utiliser correctement PDO? La première étape vient en premier, vous savez? Une recrue (qui vient d'apprendre qu'il y a plus que mysql_ *) devrait vraiment apprendre l'utilisation de la PDO de base avant même d'envisager une bibliothèque d'emballage DB personnalisée. En outre, deux doublures comme la vôtre sont un pita pour lire / comprendre / déboguer / modifier / modifier. Le code court n'est pas toujours le meilleur code.


Eh bien, bien que ce soit un peu bon, je suis sûr qu'il devrait y avoir un avertissement de toute façon, juste pour montrer la voie à suivre. Et, pour défendre mon 2 doublure, j'oserais dire que c'est bien plus amical de débogage que votre 7-doublure, il est lisible et facile à modifier. Si vous pouvez me montrer certains points faibles de ce code particulier, pas en 2-doublures en général, je l'apprécierai cependant.


@ Col.shrapnel - une fois de plus, vous avez répondu avec une réponse vraiment détaillée et constructive! Tout le monde a! J'avais certainement une meilleure compréhension de tout ce qui est de toute façon, je vais essayer les méthodes de votre et de RIHA et de voir ce que je me sens à l'aise jusqu'à ce que je comprenne plus de compréhension de tout cela. Merci!


@ Col.shrapnel Je trouve le code plus difficile à lire et à modifier, mais c'est probablement la préférence personnelle. Cependant, il ne joue pas bien avec les VC comme Git / Mercurial. Imaginez que vous devez ajouter une nouvelle ligne à la condition d'une branche de fonctionnalité. Ensuite, il y a un autre changement sur une autre branche. Maintenant, les deux branches de fonctionnalités doivent être fusionnées à la branche principale - Conflit de fusion! Amusez-vous à résoudre les mois après que vous l'avez codé et que vous avez oublié longtemps ce que le code est à propos. Avec cela à l'esprit, le code de grammage ensemble sur peu de lignes est presque toujours hors de question. Ça va te mordre quand on s'attendrale au moins. :)


@riha parlez-vous de la requête elle-même? Faites-vous ce que vous le souhaitez, le point de mon code est complet différent . Je suis désolé que tu ne l'as pas eu.


Le point de votre code est de laisser votre bibliothèque gérer la préparation de la requête et des liaisons de paramètres. Quoi qu'il en soit, arrêtons de violer les commentaires.


@riha, il n'automise pas seulement ces liaisons répétées inutiles. Il interroge également l'exécution, la manipulation des erreurs, la récupération de données, le profilage, la journalisation de la requête, la manipulation de la connexion et plus encore. allez comprendre.



5
votes

Oui,: Quelque chose est un espace réservé nommé en PDO ,? est un espace réservé anonyme. Ils vous permettent de se lier les valeurs un à une ou une fois à la fois.

Ainsi, fondamentalement, cela fait quatre options pour fournir votre requête avec des valeurs. P>

un par un avec BindValue () strong> p>

Ceci lie une valeur concrète à votre espace réservé dès que vous l'appelez. Vous pouvez même lier des chaînes codées dures telles que Bindvalue (': quelque chose', 'foo') code> si vous le souhaitez. P>

Fournir un type de paramètre est facultatif (mais suggéré). Cependant, puisque la valeur par défaut est pdo :: param_str code>, il vous suffit de le spécifier quand il n'est pas une chaîne. De plus, PDO code> prendra soin de la longueur ici - il n'y a pas de paramètre de longueur. P> xxx pré>

Je préfère habituellement cette approche. Je trouve le plus propre et le plus flexible. Em> p>

un par un avec bindaram () strong> p>

une variable est liée à votre espace réservé qui sera lu lorsque la requête est exécuté strong>, pas lorsque BindParam () est appelé. Cela peut ou peut ne pas être ce que vous voulez. Il est utile lorsque vous voulez exécuter à plusieurs reprises votre requête avec différentes valeurs. P>

try {
  $db = new PDO(...);
  $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING)
} catch (PDOException $e) {
  echo 'Oops, something went wrong with the database connection.';
}


3 commentaires

Donc, le bloc d'essai / attraper est-il similaire à une déclaration if / d'autre? Et merci d'avoir souligné les différentes options, cela devrait me donner quelque chose à jouer avec!


Une autre question rapide, quel serait utilisé le être utilisé pour, je suppose que vous le référencez pour une erreur?


Pas assez. Si une exception est lancée n'importe où dans le bloc d'essai, il aborte l'exécution du bloc d'essai et exécute le bloc de capture. $ E est l'exception qui a été lancée. Il contient le message d'erreur / code / fichier / ligne, etc. et peut être utilisé pour envoyer un courrier électronique à l'administrateur ou à vous connecter à un fichier afin que vous puissiez enquêter sur ce qui s'est mal passé. Résumé: Essayer / Catch est exceptionnellement bon pour la manipulation des erreurs. Quel jeu de mots haha.