Quelles sont les raisons techniques pour lesquelles il ne faut pas utiliser de mysql_*
fonctions? (par exemple mysql_query()
, mysql_connect()
ou mysql_real_escape_string()
)?
Pourquoi devrais-je utiliser autre chose même s'ils fonctionnent sur mon site?
S'ils ne fonctionnent pas sur mon site, pourquoi ai-je des erreurs comme
Avertissement: mysql_connect (): aucun fichier ou répertoire de ce type
Réponses:
L'extension MySQL:
Comme il est obsolète, son utilisation rend votre code moins pérenne.
Le manque de prise en charge des instructions préparées est particulièrement important car elles fournissent une méthode plus claire et moins sujette aux erreurs pour échapper et citer des données externes que de les échapper manuellement avec un appel de fonction distinct.
Voir la comparaison des extensions SQL .
la source
PHP propose trois API différentes pour se connecter à MySQL. Ce sont les
mysql
(supprimés à partir de PHP 7)mysqli
, et lesPDO
extensions.Les
mysql_*
fonctions étaient très populaires, mais leur utilisation n'est plus encouragée. L'équipe de documentation discute de la situation de la sécurité de la base de données et éduquer les utilisateurs à s'éloigner de l'extension ext / mysql couramment utilisée en fait partie (consultez php.internals: déprécier ext / mysql ).Et l'équipe de développement de PHP plus tard a pris la décision de générer des
E_DEPRECATED
erreurs lorsque les utilisateurs se connectent à MySQL, que ce soit parmysql_connect()
,mysql_pconnect()
ou la fonctionnalité de connexion implicite intégrée dansext/mysql
.ext/mysql
a été officiellement déconseillé depuis PHP 5.5 et a été supprimé depuis PHP 7 .Voir la boîte rouge?
Lorsque vous allez sur n'importe quelle
mysql_*
page de manuel de fonction, vous voyez une boîte rouge, expliquant qu'elle ne devrait plus être utilisée.Pourquoi
S'éloigner
ext/mysql
n'est pas seulement une question de sécurité, mais aussi d'avoir accès à toutes les fonctionnalités de la base de données MySQL.ext/mysql
a été construit pour MySQL 3.23 et n'a reçu que très peu d'ajouts depuis, tout en conservant la compatibilité avec cette ancienne version, ce qui rend le code un peu plus difficile à maintenir. Les fonctionnalités manquantes qui ne sont pas prises en chargeext/mysql
incluent: (à partir du manuel PHP ).Raison de ne pas utiliser la
mysql_*
fonction :Ci-dessus point cité de la réponse de Quentin
Le manque de prise en charge des instructions préparées est particulièrement important car elles fournissent une méthode plus claire et moins sujette aux erreurs pour échapper et citer des données externes que de les échapper manuellement avec un appel de fonction distinct.
Voir la comparaison des extensions SQL .
Suppression des avertissements de dépréciation
Pendant la conversion du code en
MySQLi
/PDO
, lesE_DEPRECATED
erreurs peuvent être supprimées en définissanterror_reporting
dans php.ini pour exclureE_DEPRECATED:
Notez que cela masquera également d' autres avertissements de dépréciation , qui peuvent cependant concerner d'autres choses que MySQL. ( du manuel PHP )
L'article PDO vs MySQLi: lequel utiliser? par Dejan Marjanovic vous aidera à choisir.
Et une meilleure façon est
PDO
, et j'écris maintenant unPDO
tutoriel simple .Un tutoriel PDO simple et court
Q. Ma première question était: qu'est-ce que «AOP»?
A. « PDO - PHP Data Objects - est une couche d'accès à la base de données fournissant une méthode uniforme d'accès à plusieurs bases de données.»
Connexion à MySQL
Avec
mysql_*
fonction ou on peut le dire à l'ancienne (obsolète en PHP 5.5 et supérieur)Avec
PDO
: il vous suffit de créer un nouvelPDO
objet. Le constructeur accepte les paramètres pour spécifier la source de base de donnéesPDO
constructeur de la plupart prend quatre paramètres qui sontDSN
(nom de la source de données) et le cas échéantusername
,password
.Ici, je pense que vous connaissez tout sauf
DSN
; c'est nouveau dansPDO
. ADSN
est essentiellement une chaîne d'options indiquant lePDO
pilote à utiliser et les détails de connexion. Pour plus d'informations, consultez PDO MySQL DSN .Remarque: vous pouvez également utiliser
charset=UTF-8
, mais parfois cela provoque une erreur, il est donc préférable de l'utiliserutf8
.S'il y a une erreur de connexion, il lancera un
PDOException
objet qui peut être capturé pour être manipuléException
davantage.Bonne lecture : Connexions et gestion des connexions ¶
Vous pouvez également passer plusieurs options de pilote sous forme de tableau au quatrième paramètre. Je recommande de passer le paramètre qui met
PDO
en mode exception. Étant donné que certainsPDO
pilotes ne prennent pas en charge les instructions préparées natives,PDO
exécute donc l' émulation de la préparation. Il vous permet également d'activer manuellement cette émulation. Pour utiliser les instructions natives préparées côté serveur, vous devez les définir explicitementfalse
.L'autre consiste à désactiver la préparation de l'émulation qui est activée dans le
MySQL
pilote par défaut, mais la préparation de l'émulation doit être désactivée pour une utilisation enPDO
toute sécurité.J'expliquerai plus tard pourquoi la préparation de l'émulation doit être désactivée. Pour trouver la raison, veuillez consulter cet article .
Il n'est utilisable que si vous utilisez une ancienne version
MySQL
dont je ne recommande pas.Voici un exemple de la façon dont vous pouvez le faire:
Pouvons-nous définir des attributs après la construction de PDO?
Oui , nous pouvons également définir certains attributs après la construction de PDO avec la
setAttribute
méthode:La gestion des erreurs
La gestion des erreurs est beaucoup plus simple
PDO
quemysql_*
.Une pratique courante lors de l'utilisation
mysql_*
est:OR die()
n'est pas un bon moyen de gérer l'erreur car nous ne pouvons pas gérer la chosedie
. Il terminera simplement le script brusquement, puis fera écho de l'erreur à l'écran que vous ne voulez généralement pas montrer à vos utilisateurs finaux, et permettra aux pirates sanglants de découvrir votre schéma. Alternativement, les valeurs de retour desmysql_*
fonctions peuvent souvent être utilisées en conjonction avec mysql_error () pour gérer les erreurs.PDO
offre une meilleure solution: les exceptions. Tout ce que nous faisons avecPDO
devrait être enveloppé dans untry
-catch
bloc. Nous pouvons forcerPDO
dans l'un des trois modes d'erreur en définissant l'attribut de mode d'erreur. Trois modes de gestion des erreurs sont présentés ci-dessous.PDO::ERRMODE_SILENT
. Il s'agit simplement de définir des codes d'erreur et agit à peu près de la même manière quemysql_*
lorsque vous devez vérifier chaque résultat, puis regarder$db->errorInfo();
pour obtenir les détails de l'erreur.PDO::ERRMODE_WARNING
RelevezE_WARNING
. (Avertissements d'exécution (erreurs non fatales). L'exécution du script n'est pas interrompue.)PDO::ERRMODE_EXCEPTION
: Lève des exceptions. Il représente une erreur déclenchée par PDO. Vous ne devez pas jeter unPDOException
de votre propre code. Voir Exceptions pour plus d'informations sur les exceptions en PHP. Il agit très bien commeor die(mysql_error());
, quand il n'est pas attrapé. Mais contrairement àor die()
, lePDOException
peut être attrapé et manipulé avec élégance si vous choisissez de le faire.Bonne lecture :
Comme:
Et vous pouvez l'envelopper
try
-catch
, comme ci-dessous:Vous n'avez pas à gérer avec
try
- encatch
ce moment. Vous pouvez l'attraper à tout moment approprié, mais je vous recommande fortement d'utilisertry
-catch
. Il peut également être plus judicieux de l'attraper en dehors de la fonction qui appelle lePDO
truc:De plus, vous pouvez gérer par
or die()
ou nous pouvons dire commemysql_*
, mais ce sera vraiment varié. Vous pouvez masquer les messages d'erreur dangereux en production en tournantdisplay_errors off
et en lisant simplement votre journal des erreurs.Maintenant, après avoir lu toutes les choses ci - dessus, vous pensez probablement: ce que le diable est que quand je veux juste commencer à se penchant simples
SELECT
,INSERT
,UPDATE
, ouDELETE
déclarations? Ne vous inquiétez pas, c'est parti:Sélection des données
Donc, ce que vous faites,
mysql_*
c'est:Maintenant
PDO
, vous pouvez faire ceci comme:Ou
Remarque : Si vous utilisez la méthode comme ci-dessous (
query()
), cette méthode renvoie unPDOStatement
objet. Donc, si vous voulez récupérer le résultat, utilisez-le comme ci-dessus.Dans PDO Data, il est obtenu via la
->fetch()
, une méthode de votre descripteur d'instructions. Avant d'appeler la récupération, la meilleure approche serait de dire à PDO comment vous souhaitez que les données soient récupérées. Dans la section ci-dessous, j'explique cela.Modes de récupération
Notez l'utilisation de
PDO::FETCH_ASSOC
dans le codefetch()
etfetchAll()
ci-dessus. Cela indiquePDO
de renvoyer les lignes sous forme de tableau associatif avec les noms de champ comme clés. Il existe également de nombreux autres modes de récupération que j'expliquerai un par un.Tout d'abord, j'explique comment sélectionner le mode de récupération:
Dans ce qui précède, j'ai utilisé
fetch()
. Vous pouvez aussi utiliser:PDOStatement::fetchAll()
- Renvoie un tableau contenant toutes les lignes du jeu de résultatsPDOStatement::fetchColumn()
- Renvoie une seule colonne de la ligne suivante d'un jeu de résultatsPDOStatement::fetchObject()
- Récupère la ligne suivante et la renvoie en tant qu'objet.PDOStatement::setFetchMode()
- Définissez le mode de récupération par défaut pour cette instructionMaintenant j'arrive en mode fetch:
PDO::FETCH_ASSOC
: retourne un tableau indexé par nom de colonne tel que renvoyé dans votre jeu de résultatsPDO::FETCH_BOTH
(par défaut): renvoie un tableau indexé à la fois par le nom de la colonne et le numéro de colonne indexé par 0, tel que renvoyé dans votre jeu de résultatsIl y a encore plus de choix! Lisez-les tous dans la
PDOStatement
documentation Fetch. .Obtenir le nombre de lignes :
Au lieu d'utiliser
mysql_num_rows
pour obtenir le nombre de lignes retournées, vous pouvez obtenir unPDOStatement
et fairerowCount()
, comme:Obtention du dernier ID inséré
Insérer et mettre à jour ou supprimer des instructions
Ce que nous faisons en
mysql_*
fonction, c'est:Et dans pdo, cette même chose peut être faite par:
Dans la requête ci-dessus,
PDO::exec
exécutez une instruction SQL et retourne le nombre de lignes affectées.L'insertion et la suppression seront traitées ultérieurement.
La méthode ci-dessus n'est utile que lorsque vous n'utilisez pas de variable dans la requête. Mais lorsque vous devez utiliser une variable dans une requête, n'essayez jamais comme ci-dessus et là pour l' instruction préparée ou l'instruction paramétrée .
Déclarations préparées
Q. Qu'est-ce qu'une déclaration préparée et pourquoi en ai-je besoin?
A. Une instruction préparée est une instruction SQL précompilée qui peut être exécutée plusieurs fois en envoyant uniquement les données au serveur.
Le flux de travail typique de l'utilisation d'une instruction préparée est le suivant ( cité dans Wikipedia trois points 3 ):
Préparer : le modèle d'instruction est créé par l'application et envoyé au système de gestion de base de données (SGBD). Certaines valeurs ne sont pas spécifiées, appelées paramètres, espaces réservés ou variables de liaison (étiquetées
?
ci-dessous):INSERT INTO PRODUCT (name, price) VALUES (?, ?)
Le SGBD analyse, compile et effectue l'optimisation des requêtes sur le modèle d'instruction et stocke le résultat sans l'exécuter.
1.00
pour le deuxième paramètre.Vous pouvez utiliser une instruction préparée en incluant des espaces réservés dans votre SQL. Il y en a essentiellement trois sans espaces réservés (n'essayez pas avec la variable ci-dessus), un avec des espaces réservés sans nom et un avec des espaces réservés nommés.
Q. Alors maintenant, quels sont les espaces réservés nommés et comment les utiliser?
A. Espaces réservés nommés. Utilisez des noms descriptifs précédés de deux points, au lieu de points d'interrogation. Nous ne nous soucions pas de la position / de l'ordre de valeur dans le nom de la marque de réservation:
bindParam(parameter,variable,data_type,length,driver_options)
Vous pouvez également lier à l'aide d'un tableau d'exécution:
Une autre fonctionnalité intéressante pour les
OOP
amis est que les espaces réservés nommés ont la possibilité d'insérer des objets directement dans votre base de données, en supposant que les propriétés correspondent aux champs nommés. Par exemple:Q. Alors maintenant, quels sont les espaces réservés sans nom et comment les utiliser?
A. Prenons un exemple:
et
Dans ce qui précède, vous pouvez les voir
?
au lieu d'un nom comme dans un espace réservé au nom. Maintenant, dans le premier exemple, nous affectons des variables aux différents espaces réservés ($stmt->bindValue(1, $name, PDO::PARAM_STR);
). Ensuite, nous attribuons des valeurs à ces espaces réservés et exécutons l'instruction. Dans le deuxième exemple, le premier élément du tableau va au premier?
et le second au second?
.REMARQUE : dans les espaces réservés sans nom, nous devons prendre soin du bon ordre des éléments du tableau que nous transmettons à la
PDOStatement::execute()
méthode.SELECT
,INSERT
,UPDATE
,DELETE
Préparé les requêtesSELECT
:INSERT
:DELETE
:UPDATE
:REMARQUE:
Cependant
PDO
et / ouMySQLi
ne sont pas complètement sûrs. Vérifiez la réponse Les instructions préparées par PDO sont-elles suffisantes pour empêcher l'injection SQL? par ircmaxell . Aussi, je cite une partie de sa réponse:la source
IN (...) construct
.function throwEx() { throw new Exception("You did selected not existng db"); } mysql_select_db("nonexistdb") or throwEx();
d'exception. Cela fonctionne pour lancer des exceptions.Doesn't support non-blocking, asynchronous queries
indiquez comme raison de ne pas utiliser mysql_ - vous devez également l'indiquer comme raison de ne pas utiliser PDO, car PDO ne prend pas cela en charge non plus. (mais MySQLi le supporte)Tout d'abord, commençons par le commentaire standard que nous donnons à tout le monde:
Passons en revue, phrase par phrase, et expliquons:
Ils ne sont plus entretenus et sont officiellement obsolètes
Cela signifie que la communauté PHP abandonne progressivement le support de ces très anciennes fonctions. Ils n'existeront probablement pas dans une future version (récente) de PHP! L'utilisation continue de ces fonctions peut casser votre code dans un avenir (pas si lointain).
NOUVEAU! - ext / mysql est désormais officiellement obsolète depuis PHP 5.5!
Plus récent! ext / mysql a été supprimé en PHP 7 .
Au lieu de cela, vous devriez apprendre des déclarations préparées
mysql_*
L'extension ne prend pas en charge les instructions préparées , ce qui est (entre autres) une contre-mesure très efficace contre l' injection SQL . Il a corrigé une vulnérabilité très grave dans les applications dépendantes de MySQL qui permet aux attaquants d'accéder à votre script et d'effectuer toute requête possible sur votre base de données.Pour plus d'informations, consultez Comment puis-je empêcher l'injection SQL en PHP?
Voir la boîte rouge?
Lorsque vous accédez à une
mysql
page de manuel de fonction, vous voyez un cadre rouge, expliquant qu'il ne doit plus être utilisé.Utilisez PDO ou MySQLi
Il existe des alternatives meilleures, plus robustes et bien construites, PDO - PHP Database Object , qui offre une approche OOP complète de l'interaction avec la base de données, et MySQLi , qui est une amélioration spécifique à MySQL.
la source
IN (...) construct
.Facilité d'utilisation
Les raisons analytiques et synthétiques ont déjà été évoquées. Pour les nouveaux arrivants, il y a une incitation plus importante à cesser d'utiliser les fonctions datées de mysql_.
Les API de base de données contemporaines sont simplement plus faciles à utiliser.
Ce sont surtout les paramètres liés qui peuvent simplifier le code. Et avec d' excellents tutoriels (comme vu ci-dessus), la transition vers PDO n'est pas trop difficile.
Cependant, la réécriture d'une base de code plus importante prend du temps. Raison d'être de cette alternative intermédiaire:
Fonctions pdo_ * équivalentes à la place de
mysql_ *En utilisant < pdo_mysql.php >, vous pouvez passer des anciennes fonctions mysql_ avec un minimum d'effort . Il ajoute
pdo_
des wrappers de fonction qui remplacent leursmysql_
homologues.Simplement dans chaque script d'invocation qui doit interagir avec la base de données.
include_once(
"pdo_mysql.php"
);
Supprimez le
préfixe de la fonction partout et remplacez-le parmysql_
pdo_
.mysql_
connect()
devientpdo_
connect()
mysql_
query()
devientpdo_
query()
mysql_
num_rows()
devientpdo_
num_rows()
mysql_
insert_id()
devientpdo_
insert_id()
mysql_
fetch_array()
devientpdo_
fetch_array()
mysql_
fetch_assoc()
devientpdo_
fetch_assoc()
mysql_
real_escape_string()
devientpdo_
real_escape_string()
Votre code fonctionnera de la même manière et restera généralement le même:
Et voilà.
Votre code utilise PDO.
Il est maintenant temps de l' utiliser réellement .
Les paramètres liés peuvent être faciles à utiliser
Vous avez juste besoin d'une API moins lourde.
pdo_query()
ajoute un support très facile pour les paramètres liés. La conversion de l'ancien code est simple:Déplacez vos variables hors de la chaîne SQL.
pdo_query()
.?
tant qu'espaces réservés là où les variables se trouvaient auparavant.'
guillemets simples qui contenaient précédemment des valeurs / variables de chaîne.L'avantage devient plus évident pour un code plus long.
Souvent, les variables de chaîne ne sont pas seulement interpolées en SQL, mais concaténées avec des appels d'échappement entre les deux.
Avec les
?
espaces réservés appliqués, vous n'avez pas à vous soucier de cela:N'oubliez pas que pdo_ * autorise toujours soit ou .
N'échappez simplement pas à une variable et liez-la dans la même requête.
:named
listes d'espace réservé ont également été autorisées ultérieurement.Plus important encore, vous pouvez passer des variables $ _REQUEST [] en toute sécurité derrière n'importe quelle requête. Lorsque les
<form>
champs soumis correspondent exactement à la structure de la base de données, c'est encore plus court:Tant de simplicité. Mais revenons à quelques conseils de réécriture et à des raisons techniques pour lesquelles vous voudrez peut-être vous débarrasser
et vous échapper.mysql_
Corrigez ou supprimez toute
sanitize()
fonction oldschoolUne fois que vous avez converti tous les
appels enmysql_
pdo_query
paramètres paramétrés, supprimez tous lespdo_real_escape_string
appels redondants .En particulier , vous devez fixer une
sanitize
ouclean
oufilterThis
ouclean_data
fonctions comme annoncé par des tutoriels datés sous une forme ou l'autre:Le bogue le plus flagrant ici est le manque de documentation. Plus important encore, l'ordre de filtrage était exactement dans le mauvais ordre.
L'ordre correct aurait été: déprécié en
stripslashes
tant qu'appel le plus interne, puistrim
, par la suitestrip_tags
,htmlentities
pour le contexte de sortie, et enfin enfin,_escape_string
car son application devrait précéder directement l'interpénétration SQL.Mais comme première étape, débarrassez-vous de l'
_real_escape_string
appel.Vous devrez peut-être conserver le reste de votre
sanitize()
fonction pour l'instant si votre base de données et votre flux d'application attendent des chaînes sécurisées pour le contexte HTML. Ajoutez un commentaire qu'il applique désormais uniquement le HTML s'échappant.La gestion des chaînes / valeurs est déléguée à PDO et à ses instructions paramétrées.
S'il y avait une mention
stripslashes()
dans votre fonction de désinfection, cela peut indiquer une surveillance de niveau supérieur.Cela était généralement là pour réparer les dommages (double évasion) causés par les obsolètes
magic_quotes
. Quelle est cependant la meilleure solution centralisée , pas chaîne par chaîne.Utilisez l'une des approches d' inversion de l' espace utilisateur . Supprimez ensuite le
stripslashes()
dans lasanitize
fonction.Différences entre les déclarations préparées
Lorsque vous brouillez des variables de chaîne dans les requêtes SQL, il n'est pas seulement plus complexe à suivre. C'est également un effort inutile pour MySQL de séparer à nouveau le code et les données.
Les injections SQL se produisent simplement lorsque les données se transforment en code contexte du . Un serveur de base de données ne peut pas repérer plus tard où PHP a initialement collé des variables entre les clauses de requête.
Avec les paramètres liés, vous séparez le code SQL et les valeurs de contexte SQL dans votre code PHP. Mais il n'est pas remanié dans les coulisses (sauf avec PDO :: EMULATE_PREPARES). Votre base de données reçoit les commandes SQL non modifiées et les valeurs de variable 1: 1.
Bien que cette réponse souligne que vous devez vous soucier des avantages de lisibilité de la suppression
. Il y a aussi parfois un avantage en termes de performances (INSERT répétés avec des valeurs juste différentes) en raison de cette séparation visible et technique des données / codes.mysql_
Attention, la liaison de paramètres n'est toujours pas une solution magique contre tous les injections SQL. Il gère l'utilisation la plus courante des données / valeurs. Mais ne peut pas mettre en liste blanche les identificateurs de nom / table de colonne, aider à la construction de clauses dynamiques ou simplement des listes de valeurs de tableau simples.
Utilisation de PDO hybride
Ces
pdo_*
fonctions d'encapsuleur font une API de stop-gap facile à coder. (C'est à peu près ce quiMYSQLI
aurait pu être sans le changement de signature de fonction idiosyncrasique). Ils exposent également la vraie AOP dans la plupart des cas.La réécriture ne doit pas s'arrêter à l'utilisation des nouveaux noms de fonctions pdo_. Vous pouvez effectuer une transition une par une chaque pdo_query () dans un appel $ pdo-> prepare () -> execute ().
Il est toutefois préférable de recommencer à simplifier. Par exemple, l'extraction de résultats courante:
Peut être remplacé par juste une itération foreach:
Ou mieux encore, une récupération directe et complète des baies:
Vous obtiendrez des avertissements plus utiles dans la plupart des cas que PDO ou mysql_ fournissent généralement après l'échec des requêtes.
Autres options
J'espère donc que cela a permis de visualiser certaines raisons pratiques et une voie intéressante à abandonner
.mysql_
Passer simplement à pdone le coupe pas tout à fait.
pdo_query()
est aussi juste un frontend dessus.À moins que vous n'introduisiez également une liaison de paramètres ou que vous ne puissiez utiliser autre chose à partir de la meilleure API, il s'agit d'un commutateur inutile. J'espère qu'il est décrit assez simplement pour ne pas décourager davantage les nouveaux arrivants. (L'éducation fonctionne généralement mieux que la prohibition.)
Bien qu'il soit admissible à la catégorie des choses les plus simples qui pourraient éventuellement fonctionner, il s'agit également d'un code très expérimental. Je viens de l'écrire le week-end. Il existe cependant une pléthore d'alternatives. Il suffit de google pour l' abstraction de la base de données PHP et de parcourir un peu. Il y a toujours eu et il y aura beaucoup d'excellentes bibliothèques pour de telles tâches.
Si vous souhaitez simplifier davantage l'interaction de votre base de données, des cartographes comme Paris / Idiorm valent la peine d'être essayés . Tout comme personne n'utilise plus le DOM fade en JavaScript, vous n'avez plus besoin de garder une interface de base de données brute de nos jours.
la source
pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);
fonction - à savoir:pdo_query("INSERT INTO users VALUES (?, ?, ?), $_POST); $_POST = array( 'username' => 'lawl', 'password' => '123', 'is_admin' => 'true');
pdo_real_escape_string()
<- Est-ce même une fonction réelle, je ne trouve aucune documentation pour cela? Veuillez poster une source pour cela.Les
mysql_
fonctions:la source
mysqli_
mysql_*
fonction est un shell dans les fonctions mysqlnd pour les nouvelles versions de PHP. Donc même si l'ancienne bibliothèque client n'est plus maintenue, mysqlnd est maintenu :)En parlant de raisons techniques , il n'y en a que quelques-unes, extrêmement spécifiques et rarement utilisées. Très probablement, vous ne les utiliserez jamais dans votre vie.
Je suis peut-être trop ignorant, mais je n'ai jamais eu l'occasion de les utiliser comme
Si vous en avez besoin - ce sont sans aucun doute des raisons techniques pour passer de l'extension mysql à quelque chose de plus élégant et moderne.
Néanmoins, il existe également des problèmes non techniques, qui peuvent rendre votre expérience un peu plus difficile
Ce dernier problème est un problème.
Mais, à mon avis, la solution proposée n'est pas meilleure non plus.
Il me semble un rêve trop idéaliste que tous ces utilisateurs PHP apprennent à gérer correctement les requêtes SQL à la fois. Très probablement, ils changeraient simplement mysql_ * en mysqli_ * mécaniquement, laissant l'approche la même . Surtout parce que mysqli rend l'utilisation des instructions préparées incroyablement douloureuse et gênante.
Sans oublier que les instructions natives préparées ne suffisent pas à protéger des injections SQL, et ni mysqli ni PDO ne proposent de solution.
Donc, au lieu de lutter contre cette extension honnête, je préfère lutter contre les mauvaises pratiques et éduquer les gens de la bonne manière.
En outre, il existe des raisons fausses ou non significatives, comme
mysql_query("CALL my_proc");
depuis des siècles)Le dernier est un point intéressant. Bien que mysql ext ne prenne pas en charge les instructions natives préparées, elles ne sont pas requises pour la sécurité. Nous pouvons facilement simuler des instructions préparées à l'aide d'espaces réservés gérés manuellement (comme le fait PDO):
voila est , tout est paramétré et sûr.
Mais bon, si vous n'aimez pas la case rouge dans le manuel, un problème de choix se pose: mysqli ou PDO?
Eh bien, la réponse serait la suivante:
Si, comme la grande majorité des utilisateurs de PHP, vous utilisez des appels d'API bruts directement dans le code d'application (ce qui est essentiellement une mauvaise pratique) - PDO est le seul choix , car cette extension prétend être non seulement une API, mais plutôt un semi-DAL, encore incomplet mais offre de nombreuses fonctionnalités importantes, avec deux d'entre elles, PDO se distingue de manière critique de mysqli:
Donc, si vous êtes un utilisateur PHP moyen et que vous souhaitez vous épargner une tonne de maux de tête lors de l'utilisation d'instructions préparées nativement, PDO - encore une fois - est le seul choix.
Cependant, l'AOP n'est pas non plus une solution miracle et a ses difficultés.
J'ai donc écrit des solutions pour tous les pièges courants et les cas complexes dans le wiki des balises PDO
Néanmoins, tous ceux qui parlent d'extensions manquent toujours les 2 faits importants sur Mysqli et PDO:
La déclaration préparée n'est pas une solution miracle . Il existe des identificateurs dynamiques qui ne peuvent pas être liés à l'aide d'instructions préparées. Il existe des requêtes dynamiques avec un nombre inconnu de paramètres, ce qui rend la création de requêtes difficile.
Ni mysqli_ * ni les fonctions PDO n'auraient dû apparaître dans le code de l'application.
Il devrait y avoir une couche d'abstraction entre eux et le code d'application, qui fera tout le sale boulot de liaison, de boucle, de gestion des erreurs, etc. à l'intérieur, rendant le code d'application SEC et propre. Surtout pour les cas complexes comme la construction de requêtes dynamiques.
Donc, il ne suffit pas de passer à PDO ou à mysqli. Il faut utiliser un ORM, ou un générateur de requêtes, ou n'importe quelle classe d'abstraction de base de données au lieu d'appeler des fonctions API brutes dans leur code.
Et au contraire - si vous avez une couche d'abstraction entre votre code d'application et l'API mysql - peu importe le moteur utilisé. Vous pouvez utiliser mysql ext jusqu'à ce qu'il devienne obsolète, puis réécrire facilement votre classe d'abstraction sur un autre moteur, en conservant l'intégralité du code d'application.
Voici quelques exemples basés sur ma classe safemysql pour montrer comment une telle classe d'abstraction devrait être:
Comparez cette seule ligne avec la quantité de code dont vous aurez besoin avec PDO .
Ensuite, comparez avec la quantité folle de code dont vous aurez besoin avec les instructions brutes préparées par Mysqli. Notez que la gestion des erreurs, le profilage, la journalisation des requêtes sont déjà intégrés et en cours d'exécution.
Comparez-le avec les insertions PDO habituelles, lorsque chaque nom de champ est répété six à dix fois - dans tous ces nombreux espaces réservés nommés, liaisons et définitions de requête.
Un autre exemple:
Vous pouvez difficilement trouver un exemple pour PDO pour gérer un tel cas pratique.
Et ce sera trop verbeux et très probablement dangereux.
Donc, une fois de plus - ce n'est pas seulement le pilote brut qui devrait être votre préoccupation, mais la classe d'abstraction, utile non seulement pour des exemples idiots du manuel du débutant, mais pour résoudre tous les problèmes réels.
la source
mysql_*
rend les vulnérabilités très faciles à trouver. Étant donné que PHP est utilisé par un grand nombre d'utilisateurs novices, ilmysql_*
est activement nuisible dans la pratique, même si en théorie il peut être utilisé sans accroc.everything is parameterized and safe
- il peut être paramétré, mais votre fonction n'utilise pas de véritables instructions préparées.Not under active development
seulement pour ce «0,01%» composé? Si vous construisez quelque chose avec cette fonction d'arrêt, mettez à jour votre version mysql dans un an et que vous vous retrouvez avec un système qui ne fonctionne pas, je suis sûr qu'il y a énormément de gens soudainement dans ce `` 0,01% ''. Je dirais celadeprecated
etnot under active development
sont étroitement liés. Vous pouvez dire qu'il n'y a "aucune raison [valable]", mais le fait est que lorsqu'on propose un choix entre les options,no active development
est presque aussi mauvais quedeprecated
je dirais?Il existe de nombreuses raisons, mais la plus importante est peut-être que ces fonctions encouragent les pratiques de programmation non sécurisées car elles ne prennent pas en charge les instructions préparées. Les instructions préparées aident à prévenir les attaques par injection SQL.
Lorsque vous utilisez des
mysql_*
fonctions, vous devez vous rappeler d'exécuter les paramètres fournis par l'utilisateur viamysql_real_escape_string()
. Si vous oubliez en un seul endroit ou si vous n'échappez qu'une partie de l'entrée, votre base de données peut être attaquée.L'utilisation d'instructions préparées dans
PDO
oumysqli
rendra ce type d'erreurs de programmation plus difficile à faire.la source
Parce que (entre autres raisons), il est beaucoup plus difficile de s'assurer que les données d'entrée sont nettoyées. Si vous utilisez des requêtes paramétrées, comme c'est le cas avec PDO ou mysqli, vous pouvez entièrement éviter le risque.
Par exemple, quelqu'un pourrait utiliser
"enhzflep); drop table users"
comme nom d'utilisateur. Les anciennes fonctions permettront d'exécuter plusieurs instructions par requête, donc quelque chose comme ce sale bougre peut supprimer une table entière.Si l'on devait utiliser PDO de mysqli, le nom d'utilisateur finirait par être
"enhzflep); drop table users"
.Voir bobby-tables.com .
la source
The old functions will allow executing of multiple statements per query
- non, ils ne le feront pas. Ce type d'injection n'est pas possible avec ext / mysql - la seule façon dont ce type d'injection est possible avec PHP et MySQL est lors de l'utilisation de MySQLi et de lamysqli_multi_query()
fonction. L'injection aimable qui est possible avec ext / mysql et des chaînes non échappées est des choses comme' OR '1' = '1
extraire des données de la base de données qui n'étaient pas censées être accessibles. Dans certaines situations, il est possible d'injecter des sous-requêtes, mais il n'est toujours pas possible de modifier la base de données de cette manière.Cette réponse est écrite pour montrer à quel point il est trivial de contourner le code de validation utilisateur PHP mal écrit, comment (et en utilisant quoi) ces attaques fonctionnent et comment remplacer les anciennes fonctions MySQL par une déclaration sécurisée préparée - et, fondamentalement, pourquoi les utilisateurs de StackOverflow (probablement avec beaucoup de représentants) aboient de nouveaux utilisateurs posant des questions pour améliorer leur code.
Tout d'abord, n'hésitez pas à créer cette base de données de test mysql (j'ai appelé mine prep):
Cela fait, nous pouvons passer à notre code PHP.
Supposons que le script suivant soit le processus de vérification pour un administrateur sur un site Web (simplifié mais fonctionne si vous le copiez et l'utilisez pour les tests):
Semble assez légitime à première vue.
L'utilisateur doit saisir un identifiant et un mot de passe, non?
Brillant, ne saisissez pas ce qui suit:
et le soumettre.
La sortie est la suivante:
Super! Fonctionnant comme prévu, essayons maintenant le nom d'utilisateur et le mot de passe réels:
Incroyable! Salut tous les cinq, le code a correctement vérifié un administrateur. C'est parfait!
Eh bien pas vraiment. Disons que l'utilisateur est une petite personne intelligente. Disons que la personne est moi.
Entrez les informations suivantes:
Et la sortie est:
Félicitations, vous venez de m'autoriser à entrer dans votre section réservée aux administrateurs super-protégés avec moi en entrant un faux nom d'utilisateur et un faux mot de passe. Sérieusement, si vous ne me croyez pas, créez la base de données avec le code que j'ai fourni, et exécutez ce code PHP - qui à première vue semble vraiment vérifier le nom d'utilisateur et le mot de passe plutôt bien.
Donc, en réponse, c'est pourquoi vous êtes crié à.
Alors, jetons un coup d'œil à ce qui n'a pas fonctionné et pourquoi je viens d'entrer dans votre grotte de super-administrateurs uniquement. J'ai pris une supposition et j'ai supposé que vous ne faisiez pas attention à vos entrées et les ai simplement transmises directement à la base de données. J'ai construit l'entrée de manière à MODIFIER la requête que vous exécutiez réellement. Alors, qu'est-ce que c'était censé être, et qu'est-ce que cela a fini par être?
C'est la requête, mais lorsque nous remplaçons les variables par les entrées réelles que nous avons utilisées, nous obtenons ce qui suit:
Voyez comment j'ai construit mon «mot de passe» pour qu'il ferme d'abord le guillemet simple autour du mot de passe, puis introduise une toute nouvelle comparaison? Ensuite, juste pour des raisons de sécurité, j'ai ajouté une autre "chaîne" afin que le guillemet simple soit fermé comme prévu dans le code que nous avions à l'origine.
Cependant, il ne s'agit pas de vous crier dessus maintenant, il s'agit de vous montrer comment sécuriser votre code.
D'accord, alors qu'est-ce qui a mal tourné, et comment pouvons-nous y remédier?
Il s'agit d'une attaque par injection SQL classique. L'un des plus simples d'ailleurs. À l'échelle des vecteurs d'attaque, il s'agit d'un tout-petit qui attaque un tank - et gagne.
Alors, comment pouvons-nous protéger votre section d'administration sacrée et la rendre agréable et sécurisée? La première chose à faire sera de cesser d'utiliser ceux qui sont vraiment anciens et obsolètes
mysql_*
. Je sais, vous avez suivi un tutoriel que vous avez trouvé en ligne et ça marche, mais c'est vieux, c'est dépassé et en l'espace de quelques minutes, je viens de le dépasser sans même transpirer.Maintenant, vous avez les meilleures options pour utiliser mysqli_ ou PDO . Je suis personnellement un grand fan de PDO, donc j'utiliserai PDO dans le reste de cette réponse. Il y a des avantages et des inconvénients, mais personnellement, je trouve que les avantages l'emportent de loin sur les inconvénients. Il est portable sur plusieurs moteurs de base de données - que vous utilisiez MySQL ou Oracle ou à peu près n'importe quoi - juste en changeant la chaîne de connexion, il possède toutes les fonctionnalités de fantaisie que nous voulons utiliser et il est agréable et propre. J'aime propre.
Maintenant, regardons à nouveau ce code, cette fois écrit à l'aide d'un objet PDO:
Les principales différences sont qu'il n'y a plus de
mysql_*
fonctions. Tout se fait via un objet PDO, deuxièmement, il utilise une instruction préparée. Maintenant, quelle est la déclaration préparatoire que vous demandez? C'est un moyen de dire à la base de données avant d'exécuter une requête, quelle est la requête que nous allons exécuter. Dans ce cas, nous disons à la base de données: "Salut, je vais exécuter une instruction select voulant id, userid et passer à partir des utilisateurs de la table où l'ID utilisateur est une variable et la passe est également une variable.".Ensuite, dans l'instruction execute, nous transmettons à la base de données un tableau avec toutes les variables qu'elle attend maintenant.
Les résultats sont fantastiques. Essayons à nouveau ces combinaisons de nom d'utilisateur et de mot de passe:
L'utilisateur n'a pas été vérifié. Impressionnant.
Que diriez-vous:
Oh, je suis juste un peu excité, ça a marché: le chèque a réussi. Nous avons un administrateur vérifié!
Maintenant, essayons les données qu'un type intelligent entrerait pour essayer de dépasser notre petit système de vérification:
Cette fois, nous obtenons ce qui suit:
C'est pourquoi vous êtes crié lors de la publication de questions - c'est parce que les gens peuvent voir que votre code peut être contourné sans même essayer. Veuillez utiliser cette question et réponse pour améliorer votre code, le rendre plus sûr et utiliser les fonctions actuelles.
Enfin, cela ne veut pas dire que c'est du code PARFAIT. Il y a beaucoup d'autres choses que vous pourriez faire pour l'améliorer, utilisez des mots de passe hachés par exemple, assurez-vous que lorsque vous stockez des informations sensibles dans la base de données, vous ne les stockez pas en texte brut, avez plusieurs niveaux de vérification - mais vraiment, si vous changez simplement votre ancien code propice à l'injection en ceci, vous serez BIEN en train d'écrire du bon code - et le fait que vous soyez arrivé jusqu'ici et que vous lisez encore me donne un espoir que vous n'implémenterez pas seulement ce type de code lors de l'écriture de vos sites Web et applications, mais que vous puissiez sortir et rechercher ces autres choses que je viens de mentionner - et plus encore. Écrivez le meilleur code possible, pas le code le plus basique qui fonctionne à peine.
la source
mysql_*
soi, ce n'est pas dangereux, mais cela promeut le code non sécurisé via de mauvais didacticiels et l'absence d'une API de préparation de déclaration appropriée.L'extension MySQL est la plus ancienne des trois et était la manière originale utilisée par les développeurs pour communiquer avec MySQL. Cette extension est désormais déconseillée au profit des deux autres alternatives en raison des améliorations apportées aux nouvelles versions de PHP et de MySQL.
MySQLi est l'extension «améliorée» pour travailler avec les bases de données MySQL. Il tire parti des fonctionnalités disponibles dans les versions plus récentes du serveur MySQL, expose à la fois une interface orientée fonction et une interface orientée objet au développeur et fait quelques autres astuces.
PDO propose une API qui consolide la plupart des fonctionnalités qui étaient auparavant réparties sur les principales extensions d'accès à la base de données, à savoir MySQL, PostgreSQL, SQLite, MSSQL, etc. L'interface expose des objets de haut niveau pour que le programmeur puisse travailler avec les connexions, les requêtes et les les ensembles de résultats et les pilotes de bas niveau effectuent la communication et la gestion des ressources avec le serveur de base de données. Beaucoup de discussions et de travaux sont en cours dans PDO et il est considéré comme la méthode appropriée pour travailler avec des bases de données dans un code moderne et professionnel.
la source
Je trouve les réponses ci-dessus très longues, donc pour résumer:
Source: Présentation de MySQLi
Comme expliqué dans les réponses ci-dessus, les alternatives à mysql sont mysqli et PDO (PHP Data Objects).
MySQLi et PDO ont été introduits dans PHP 5.0, tandis que MySQL a été introduit avant PHP 3.0. Un point à noter est que MySQL est inclus dans PHP5.x bien que déconseillé dans les versions ultérieures.
la source
Il est possible de définir presque toutes les
mysql_*
fonctions en utilisant mysqli ou PDO. Il suffit de les inclure au-dessus de votre ancienne application PHP, et cela fonctionnera sur PHP7. Ma solution ici .la source
Les fonctions similaires à celle
mysql_connect()
-cimysql_query()
sont les fonctions PHP ie (PHP 4) de la version précédente et ne sont plus utilisées.Celles-ci sont remplacées par
mysqli_connect()
, demysqli_query()
même dans la dernière version de PHP5.C'est la raison de l'erreur.
la source
MySQL déconseillé en PHP 5.5.0, et supprimé en PHP 7.0.0. Pour une grande et ancienne application, il est difficile de rechercher et de remplacer chaque fonction.
Nous pouvons utiliser les fonctions MySQL en créant une fonction wrapper pour chacune ci-dessous exécute du code. Cliquez ici
la source
Les fonctions mysql_ * ont été dépréciées (à partir de PHP 5.5 ) étant donné que de meilleures fonctions et structures de code ont été développées. Le fait que la fonction soit obsolète signifie que plus aucun effort ne sera fait pour l'améliorer en termes de performances et de sécurité, ce qui signifie qu'elle est moins à l'épreuve du temps .
Si vous avez besoin de plus de raisons:
la source