Débat : Magic Quote pour ou contre ? - PHP - Programmation
Marsh Posté le 16-02-2007 à 12:20:15
c'te vieux débat
Donc AMA, ça pue. Dans l'ordre préférer PDO ou mysqli, une librairie d'abstraction type adodb, au pire du pire sprintf.
Marsh Posté le 16-02-2007 à 12:30:23
ReplyMarsh Posté le 16-02-2007 à 14:22:42
ReplyMarsh Posté le 16-02-2007 à 15:08:38
MagicBuzz a écrit : c'est de la merde en branche. il faut utiliser des requêtes paramétrées. |
mais encore
Marsh Posté le 16-02-2007 à 15:18:10
nycius a écrit : mais encore |
Des requêtes préparées. Tu as ça dans PDO (entre autres).
Marsh Posté le 16-02-2007 à 15:19:26
exemple de la doc:
Exemple 1830. Insertions répétitives en utilisant les requêtes préparées
Cet exemple effectue une requête INSERT en y substituant un nom et une valeur pour les marqueurs nommés.
Code :
|
Marsh Posté le 16-02-2007 à 16:19:45
nycius a écrit : Pour l'instant jutilise mysql_real_escape_string() mais c'est super relou |
C'est peut-être relou, mais c'est la seule méthode valable avec l'extension mysql. magic_quotes va utiliser la fonction addslashes, qui ne quote pas comme il faut pour une base mysql, et ne tient pas compte du charset. Genre avec un charset multibyte genre GBKmachin (c'est pour du chinois), addslashes va "omettre" certains quotes, et donc ne va strictement rien faire *boom*.
Ensuite magic_quotes va quoter les valeurs, sauf qu'on n'a pas toujours besoin de ça : affichage dans un document XML, insertion dans un SGBD qui demande un escape différent, affichage dans un fichier texte, tous on des besoins différents... Donc à jeter définitivement.
Et c'est tellement nul, que la directive a été enlevée de PHP6. Donc autant prendre de suite les bonnes habitudes, et coder sans. On oubliera pas donc, en début de chaque script, nettoyer les superglobales genre $_GET, $_POST etc. si jamais magic_quotes_gpc est actif (grâce à stripslashes et get_magic_quotes_gpc)
Marsh Posté le 16-02-2007 à 16:23:54
Exact, j'ai une class mysql dont je me sers tj, je vais essayer de placer directement dans la classe une solution comme ca pour eviter de passer sur chaque variable $_GET, $_POST un mysql_real_escape_string()
Marsh Posté le 16-02-2007 à 22:08:28
MagicBuzz a écrit : c'est de la merde en branche. il faut utiliser des requêtes paramétrées. |
Tout ce que j'ai pu lire sur les requetes preparées indiquent qu'elles sont intéressantes lorsqu'une meme requete est appelée plusieurs ( avec ou sans parametres ). Pas dans tous les cas.
Marsh Posté le 16-02-2007 à 22:27:30
si, elles sont intéressantes dans tous les cas parceque :
1/ facilement maintenable : le SQL est totalement séparé de la construction des paramètres
2/ PHP fait du pooling de connexion inter-sessions. c'est à dire qu'une même requête paramétrée peut être appelée par deux utilisateurs différents : du coup toute requête peut être considérée comme utilisée souvent, même si elle n'est présente qu'une fois dans une seule page
3/ c'est ce qui offre la protection la plus sûre contre le SQL Injection, et autres exploit des SGBD. ceci dit, l'implémentation d'ADO est encore meilleure, puisqu'elle oblige à typer les paramètres, ce qui permet de les valider avant d'effectuer la requête (avec cmd.Prepare()) ce qui permet notamment d'éviter un aller-retour avec le SGBD si la requête a des paramètres invalides.
Marsh Posté le 17-02-2007 à 21:15:06
MagicBuzz a écrit : si, elles sont intéressantes dans tous les cas parceque : |
Tu veux dire que si tu fais une requete préparer il n'y as pas besoin de passer par du mysql_real_escape_string? Ou alors tu dois quand même le faire en plus ... Et dans ce cas je vois pas çe que ça change niveau securité
Marsh Posté le 17-02-2007 à 21:31:26
esox_ch a écrit : Tu veux dire que si tu fais une requete préparer il n'y as pas besoin de passer par du mysql_real_escape_string? Ou alors tu dois quand même le faire en plus ... Et dans ce cas je vois pas çe que ça change niveau securité |
normalement, c'est pas a toi de le faire, c'est l'objet qui gere les requetes qui le doit le faire quand tu lui passe une chaine de caractère ...
Marsh Posté le 17-02-2007 à 22:13:42
esox_ch a écrit : Tu veux dire que si tu fais une requete préparer il n'y as pas besoin de passer par du mysql_real_escape_string? Ou alors tu dois quand même le faire en plus ... Et dans ce cas je vois pas çe que ça change niveau securité |
Non seulement tu n'as pas à le faire, mais surtout, si tu le fait, tu corromps les données.
En fait, une requête paramétrée est passée "telle quelle" au SGBD, c'est à dire avec le nom des variables genre ":valeur".
Et en même temps, dans une zone typée, la valeur de ta variable est envoyée. Ainsi, si ta variable de type "texte" contient des caractères "dangereux", alors il n'y a aucun souci, c'est bien la valeur qui est transmise au SGDB et non sa représentation littérale.
En gros, t'as cette requête :
select * from utilisateur where login = '$login'
Si $login contient la séquence suivante (SQL Injection) :
'; drop table utilisateur; --
Sans protection, on se retrouve avec ça :
select * from utilisateur where login = ''; drop table utilisateur; --'
=> Pas bien du tout
Avec MagicQuote tu te retrouves avec ça :
select * from utilisateur where login = '\'; drop table utilisateur; --'
=> A noter qu'avec SQL Server par exemple, proutch, aucune protection car le \ n'est pas du tout compté comme un caractère d'échappement.
Avec RealEscapeStringMachin :
select * from utilisateur where login = '''; drop table utilisateur; --'
=> La quote est correctement échappée, on effectue donc une recherche sur : (qui reste à échapper par le SGBD au moment de l'exécution)
''; drop table utilisateur
Avec les requêtes paramétrée, la requête exécutée est :
select * from utilisateur where login = :login
Plus une information disant que :login est de type texte et contient la valeur '; drop table utilisateur; --
Ce qui revient au même sauf que...
-> C'est la requête "sans la valeur" qui est réellement exécutée. Donc si la requête est utilisée régulièrement, même avec des valeurs différentes, le SGBD va pouvoir largement optimiser les performances de la requête car son plan d'ecution est déjà en mémoire (alors que si on passe par une requête littérale, le SGBD réanalyse tout à chaque fois qu'il y a le moindre caractère qui change dans la requête SQL)
-> Que le SGBD demande à échapper les quotes à la sauce ANSI ou à la sauce SQL, ou de n'importe quelle autre façon, on n'a aucun risque d'attaque, puisqu'on n'utilise pas d'échappement !
-> Mieux : si tu filtres sur un datetime ou un float, le problème récurent vient de la réprésentation de la date (DD/MM/YYYY, MM/DD/YYYY, YYYY-MM-DD, YYYYMMDD, ... ?) ou des problèmes de virgules, points et autres séparateurs décimaux, de milliers ou symboles monétaires, position du signe -... C'est source de tous les malheurs avec les systèmes "classiques". Avec une requête paramétrée, on s'en fout, c'est un entier, un float, un ce que tu veux, qui est directement envoyé depuis la représentation interne dans PHP vers ton SGBD. Ainsi tu peux avoir une appli écrite par un français qui va tourner sur un serveur chinois configuré en russe, t'as pas à te soucier de toute la merde classique liée aux locales pour le représentation des nombres et dates. Je ne parle pas non plus des caractères spéciaux qu'on trouve dans les charsets exotiques, qui peuvent être mal reconnus lors de l'échappement des quotes ASCII.
Bref, y'a pas plus sûr, et même si la syntaxe est un peu plus lourde, au final c'est bien moins chiant à utiliser.
En gros :
- sûr
- plus performant
- plus facile à maintenir
- évite les problèmes de localisation
Vraiment, c'est à se demander pourquoi tout le monde le boude (du moins personne ne connait).
Je viens de passer la semaine à corriger un site en C# truffée de conneries de requêtes littérales... Ah ben d'un coup tout le monde pouvait se servir de tout le site (parceque sinon, les gens plantaient certaines pages parceque ces pages étaient configurées pour travailler avec la locale du navigateur client... et tout le monde n'était pas configuré en américain). On a sécurisé le bignou, et le serveur Oracle s'est senti revivre quand les requêtes de 200 lignes sont devenues tout le temps pareilles plutôt que différentes à chaque exécution.
Bref, ce système fait ses preuves depuis 10 ans sur tous les langages, faut l'utiliser
Marsh Posté le 17-02-2007 à 22:41:41
Mais c'est associé à php que depuis la v5 il me semble, et MySql a mis longtemps à se mettre à jour sur cet aspect là aussi...
Par contre, ya pas à chier, ça dépote! Et ça rassure aussi, ce qui est important avec un langage aussi lâche que php...
Marsh Posté le 17-02-2007 à 23:09:14
Merci magic pour cette explication ...
Je vais donc implementer ça dans mon CMS dès que j'aurai le temps
Marsh Posté le 17-02-2007 à 23:34:10
Histoire de mettre en valeur à la fois le problème des "." et des ",", ainsi les performances, je viens d'écrire un petit programme tout con en C# qui tape dans SQL Server (ouais, c'est limite HS, mais c'est le principe qui compte, et c'est le même).
J'ai volontairement mis le test des requêtes paramétrées en premier, puisqu'il sera pénalisé par l'absence totale de cache dans la connexion (ceci dit c'est vrai seulement pour les premières requêtes, puisqu'ensuite le pooling fait son travail).
Le résultat est relativement édifiant... Non seulement on n'a pas besoin de gérer le coup des virgules pour une variable de type chaîne calculée à partir d'un float -me suis épaté tout seul sur ce coup- mais en plus environ trois fois plus rapide !
La table "test" contient :
- Une clé primaire organisée en cuslter "id" de type numeric(18,0)
- Un champ indexé "strval" de type nvarchar(50)
- Un champ indexé "nbrval" de type numeric(18,9)
La table contient 5000 lignes, où strval et nbrval contienent des nombres aléatoires compris entre 0 et 1 différents.
Code :
|
Résultat :
|
On peut sans problème s'attendre à des gains similaires avec n'importe quel autre langage/SGBD, puisqu'il ne s'agit pas ici d'exploiter des spécificités particulières à .NET ou SQL Server, mais bel et bien les avantages connus de ce système de passage de valeurs aux requêtes.
Marsh Posté le 17-02-2007 à 23:59:23
Ah ouais, 3 fois plus rapide quand même... Pensais po que ça montait autant
PS : par contre c'est quoi ce code pourri ?
Marsh Posté le 18-02-2007 à 07:41:59
Il a quoi mon code ?
Sinon, pour le coup des 3x plus rapide, il faut prendre avec des pincettes. Ici, les requêtes sont simples et la base petite. Donc le temps du calcul du plan d'exécution est considérable par rapport au temps d'exécution de la requête. Idem, je ne récupère aucun volume à chaque fois, puisque je fais un COUNT. Dans une application "normale", évidement, que ce soit des requêtes paramétrées ou non, le temps de recherche des données, puis le rappatriement du résultat entre le SGBD et le serveur d'appli est évidement fixe, et plus important que dans mon test. Le gain sera donc moins flagrant. Par contre, comme le montre cet exemple, il existe bel et bien.
Marsh Posté le 18-02-2007 à 09:55:42
petite précision par rapport a tout ca.
Outre le gain sur le calcul du plan d'exécution c'est surtout le compilation de la requete qui ne sera plus a refaire du coté sgbd, car si je me souviens bien un des premiers tests du sgbd est de hascoder la requete et de voir si il n'a pas déja une version égale en cache
et donc l'interet de: "select * from table where champ= :1" est que la requete ne varie pas suivant le critère de recherche.
par rapport a :
"select * from table where champ= 'toto'"
"select * from table where champ= ' toto'"
"select * from table where champ= 'Toto'"
"select * from table where champ= 'TOTO'"
Pour le recalcul du plan d'exécution la a mon avis c'est moins clair vu que les statistiques peuvent jouer.
Marsh Posté le 18-02-2007 à 12:08:40
lkolrn a écrit : Mais c'est associé à php que depuis la v5 il me semble, et MySql a mis longtemps à se mettre à jour sur cet aspect là aussi... |
Faut pas oublier qu'il n'y a pas que mysql dans la vie...
Marsh Posté le 18-02-2007 à 12:09:34
MagicBuzz a écrit : Il a quoi mon code ? |
Sur un exemple standard ca donne quoi ?
Marsh Posté le 18-02-2007 à 13:06:07
casimimir a écrit : Pour le recalcul du plan d'exécution la a mon avis c'est moins clair vu que les statistiques peuvent jouer. |
ouais, abus de langage : quand je parle de calcul du plan, ça inclu la compilation de la requête
Marsh Posté le 18-02-2007 à 13:07:04
nycius a écrit : Sur un exemple standard ca donne quoi ? |
ben j'ai pas d'exemple d'application pourrave écrite sans requêtes paramétrée. donc j'ai pas de cas réel à te proposer pour effectuer une comparaison
Marsh Posté le 18-02-2007 à 18:58:15
un like et un between, c'est tout ce qu'il y a de plus classique
je vois pas ce que vous leur voulez à mes requêtes
Marsh Posté le 18-02-2007 à 21:21:39
skeye a écrit : Faut pas oublier qu'il n'y a pas que mysql dans la vie...:o |
Sauf quand on rejoint une équipe de dev. qui pensait que si, justement
'ai bien fait de partir moa...
Marsh Posté le 19-02-2007 à 08:07:20
Attention aussi aux clauses LIKE. Imaginez vous avec dans votre appli un truc où l'utilisateur saisie le début d'une string, et ça renvoie tout les strings correspondantes. Si le mec veut inclure un % ou un _, ça foire évidemment (ce sont des wildcards). Là, mysql_real_escape_string ne servira strictement à rien... Il faut en plus échapper donc % et _, du style :
Code : |
Si le mec tape %_'pouet', on aura bien :
Code :
|
Marsh Posté le 16-02-2007 à 12:15:54
Salut,
Etes vous pour on contre l'utilisation de magic quote ? notement pour les soucis d'injections SQL
Et si oui pourquoi ?