web-dev-qa-db-fra.com

Pourquoi PDO est-il meilleur pour échapper aux requêtes / chaînes de requêtes MySQL que mysql_real_escape_string?

On m'a dit que je ferais mieux d'utiliser PDO pour l'échappement MySQL, plutôt que mysql_real_escape_string.

Peut-être que je passe une journée sans cervelle (ou c'est peut-être le fait que je ne suis aucunement un programmeur naturel, et je suis encore très au stade de débutant en ce qui concerne PHP), mais ayant vérifié le PHP manuel et lu l'entrée sur PDO , je ne suis toujours pas plus clair sur ce qu'est réellement PDO et pourquoi c'est mieux que d'utiliser mysql_real_escape_string. Cela peut être dû au fait que je n'ai pas vraiment compris les complexités de OOP pour l'instant (je suppose que c'est quelque chose à voir avec OOP), mais autre que le fait que les variables et les valeurs de tableau semble avoir un colon devant eux, je ne sais toujours pas ce que c'est réellement et comment vous l'utilisez (et pourquoi c'est mieux que mysql_real_escape_string. (Cela peut aussi avoir quelque chose à voir avec le fait que je n'ai pas vraiment une compréhension claire de ce que sont les "classes", donc quand je lis "classe PDO", je ne suis pas vraiment plus sage).

Après avoir lu n article ou deux sur le bit 'Developer Zone' du site MySQL, je ne suis toujours pas plus clair. Comme je ne peux même pas comprendre ce que c'est en ce moment, je pense que l'utiliser est probablement un peu au-delà de moi en ce moment, mais je suis toujours intéressé à élargir mes études et à découvrir comment je pourrais améliorer les choses.

Quelqu'un pourrait-il m'expliquer en "anglais simple" ce qu'est l'AOP (ou me diriger vers quelque chose sur le sujet écrit en anglais simple), et comment vous y prendriez?

48
BlissC

Comme les réponses actuelles entrent dans les détails alors que votre question vise davantage un aperçu général, je vais l'essayer:

Les classes PDO visent à encapsuler toutes les fonctionnalités nécessaires pour interagir avec une base de données. Ils le font en définissant des "méthodes" (salon OO pour les fonctions) et des "propriétés" (salon OO pour les variables). Vous les utiliseriez comme remplacement complet de toutes les fonctions "standard" que vous utilisez maintenant pour parler à une base de données.

Ainsi, au lieu d'appeler une série de fonctions 'mysql_doSomething ()', en stockant leurs résultats dans vos propres variables, vous 'instancieriez' un objet de la classe PDO ('class' = définition abstraite, 'object' = concret, instance utilisable d'une classe) et appeler des méthodes sur cet objet pour faire de même.

Par exemple, sans AOP, vous feriez quelque chose comme ceci:

// Get a db connection
$connection = mysql_connect('someHost/someDB', 'userName', 'password');
// Prepare a query
$query = "SELECT * FROM someTable WHERE something = " . mysql_real_escape_string($comparison) . "'";
// Issue a query
$db_result = mysql_query($query);
// Fetch the results
$results = array();
while ($row = mysql_fetch_array($db_result)) {
  $results[] = $row;
}

alors que ce serait l'équivalent en utilisant PDO:

// Instantiate new PDO object (will create connection on the fly)
$db = new PDO('mysql:dbname=someDB;Host=someHost');
// Prepare a query (will escape on the fly)
$statement = $db->prepare('SELECT * FROM someTable WHERE something = :comparison');
// $statement is now a PDOStatement object, with its own methods to use it, e.g.
// execute the query, passing in the parameters to replace
$statement->execute(array(':comparison' => $comparison));
// fetch results as array
$results = $statement->fetchAll();

Donc, à première vue, il n'y a pas beaucoup de différence, sauf dans la syntaxe. Mais la version PDO présente certains avantages, le plus important étant l'indépendance de la base de données:

Si vous avez besoin de parler à une base de données PostgreSQL à la place, vous ne feriez que mysql: En pgsql: Dans l'appel instanciant new PDO(). Avec l'ancienne méthode, vous devez parcourir tout votre code, en remplaçant toutes les fonctions 'mysql_doSomething ()' par leur équivalent 'pg_doSomthing ()' (en vérifiant toujours les différences potentielles dans la gestion des paramètres). Il en serait de même pour de nombreux autres moteurs de base de données pris en charge.

Donc, pour revenir à votre question, PDO vous donne simplement une manière différente de réaliser les mêmes choses, tout en offrant des raccourcis/améliorations/avantages. Par exemple, l'échappement se produirait automatiquement de la manière appropriée requise pour le moteur de base de données que vous utilisez. De plus, la substitution de paramètres (empêche les injections SQL, non illustrées dans l'exemple) est beaucoup plus facile, ce qui la rend moins sujette aux erreurs.

Vous devriez lire sur certains OOP basics pour avoir une idée des autres avantages.

57
Henrik Opel

Je ne suis pas très familier avec PDO, mais il existe une distinction entre les "instructions préparées" et les chaînes échappées. L'échappement consiste à supprimer les chaînes de caractères non autorisées de la requête, mais les instructions préparées consistent à indiquer à la base de données quel type de requête à attendre .

Une requête comporte plusieurs parties

Pensez-y de cette façon: lorsque vous envoyez une requête à la base de données, vous lui dites plusieurs choses distinctes. Une chose pourrait être, par exemple, "Je veux que vous fassiez une sélection." Un autre pourrait être "le limiter aux lignes où le nom d'utilisateur est la valeur suivante".

Si vous créez une requête sous forme de chaîne et la remettez à la base de données, elle ne connaît aucune des parties jusqu'à ce qu'elle obtienne la chaîne terminée. Vous pourriez faire ceci:

'SELECT * FROM transactions WHERE username=$username'

Quand il obtient cette chaîne, il doit l'analyser et décider "c'est un SELECT avec un WHERE".

Mélanger les pièces

Supposons qu'un utilisateur malveillant entre son nom d'utilisateur sous la forme billysmith OR 1=1. Si vous ne faites pas attention, vous pourriez mettre cela dans votre chaîne, ce qui entraînera:

'SELECT * FROM transactions WHERE username=billysmith OR 1=1'

... qui retournerait toutes les transactions pour tous les utilisateurs , car 1 est toujours égal à 1. Oups, vous avez été piraté!

Tu vois ce qui s'est passé? La base de données ne savait pas à quelles parties s'attendre dans votre requête , elle a donc simplement analysé la chaîne. Il n'était pas surpris que le WHERE ait un OR, avec deux conditions qui pouvaient le satisfaire.

Garder les pièces droites

Si seulement il avait su à quoi s'attendre , à savoir un SELECT dont WHERE n'avait qu'une seule condition, le malveillant l'utilisateur ne pouvait pas l'avoir trompé.

Avec une déclaration préparée, vous pouvez lui donner cette attente correcte. Vous pouvez dire à la base de données "Je suis sur le point de vous envoyer un SELECT, et ça va être limité aux lignes WHERE username = une chaîne que je vais vous donner. C'est tout - il n'y a pas d'autres parties à la requête. Es-tu prêt? OK, voici la chaîne à comparer au nom d'utilisateur. "

Avec cette attente, la base de données ne serait pas dupe: elle ne retournerait que les lignes où la colonne username contient la chaîne réelle 'billysmith OR 1 = 1.' Si personne n'a ce nom d'utilisateur, il ne retournerait rien.

Autres avantages des relevés préparés

En plus des avantages en matière de sécurité, les déclarations préparées présentent quelques avantages en termes de vitesse:

  • Ils peuvent être réutilisés avec différents paramètres, ce qui devrait être plus rapide que de créer une nouvelle requête à partir de zéro, car la base de données sait déjà essentiellement ce que vous êtes sur le point de demander. Il a déjà construit son "plan de requête".
  • Certaines bases de données (Postgres en est une, je pense) commenceront à faire un plan de requête dès qu'elles recevront l'instruction préparée - avant que vous ayez réellement envoyé les paramètres à utiliser avec. Vous pouvez donc voir une accélération même sur la première requête.

Pour une autre explication, voir la réponse de Theo ici .

40
Nathan Long

Contrairement à mysql_real_escape_string, PDO vous permet d'appliquer un type de données.

<?php
/* Execute a prepared statement by binding PHP variables */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < :calories AND colour = :colour');
$sth->bindParam(':calories', $calories, PDO::PARAM_INT);
$sth->bindParam(':colour', $colour, PDO::PARAM_STR, 12);
$sth->execute();
?>

Notez que dans l'exemple ci-dessus, le premier paramètre, calories, doit être un entier (PDO :: PARAM_INT).

Deuxièmement, pour moi, les requêtes paramétrées PDO sont plus faciles à lire. Je préfère lire:

SELECT name FROM user WHERE id = ? AND admin = ? 

que

SELECT name FROM user WHERE id = mysql_real_escape_string($id) AND admin = mysql_real_escape_string($admin);

Troisièmement, vous n'avez pas à vous assurer de bien citer les paramètres. AOP s'en charge. Par exemple, mysql_real_query_string:

SELECT * FROM user WHERE name = 'mysql_real_escape_string($name)' //note quotes around param

contre

SELECT * FROM user WHERE name = ?

Enfin, PDO vous permet de porter votre application sur une autre base de données sans changer vos appels de données PHP.

16
Cory House

imaginez que vous écrivez quelque chose comme:

$query = 'SELECT * FROM table WHERE id = ' . mysql_real_escape_string($id);

cela ne vous évitera pas les injections, car $ id pourrait être 1 OR 1=1 et vous obtiendrez tous les enregistrements de la table. vous devez convertir $ id dans le bon type de données (int dans ce cas)

pdo a un autre avantage, et c'est l'interchangeabilité des backends de base de données.

14
knittl

Pourquoi PDO est-il meilleur pour échapper aux requêtes/chaînes de requêtes MySQL que mysql_real_escape_string?

Tout simplement parce que "s'échapper" seul n'a aucun sens.
De plus, c'est différent incomparable compte.

Le seul problème avec l'échappement est que tout le monde se trompe, en l'assumant comme une sorte de "protection".
Tout le monde dit "J'ai échappé à mes variables" avec la signification "J'ai protégé ma requête".
Alors que s'échapper seul n'a rien à voir avec la protection du tout.

La protection peut être grossièrement atteinte en cas de je me suis échappé et ai cité mes données, mais elle n'est pas applicable partout, pour les identifiants par exemple (ainsi que les AOP, soit dit en passant).

Donc, la réponse est:

  • PDO, lors de l'échappement pour les valeurs liées, applique non seulement l'échappement mais aussi la citation - c'est pourquoi c'est mieux.
  • "échapper" n'est pas synonyme de "protection". "échapper + citer" l'est en gros.
  • mais pour certaines parties de requête, les deux méthodes ne sont pas applicables.
3
Your Common Sense

En plus d'empêcher l'injection SQL, PDO vous permet de préparer une requête une fois et de l'exécuter plusieurs fois. Si votre requête est exécutée plusieurs fois (dans une boucle, par exemple), cette méthode devrait être plus efficace (je dis "devrait l'être", car il semble que ce ne soit pas toujours le cas sur les anciennes versions de MySQL). La méthode prepare/bind est également plus conforme aux autres langages avec lesquels j'ai travaillé.

3
Wes