Pourquoi ne devrais-je pas utiliser les fonctions mysql * en PHP?
quelles sont les raisons techniques pour lesquelles on ne devrait pas utiliser les fonctions mysql_*
? (par exemple mysql_query()
, mysql_connect()
ou mysql_real_escape_string()
)?
Pourquoi devrais-je utiliser quelque chose d'autre même s'ils travaillent sur mon site?
S'ils ne fonctionnent pas sur mon site, pourquoi j'ai des erreurs comme
Warning: mysql_connect(): Aucun fichier ou répertoire de
14 réponses
L'extension MySQL:
- n'Est pas en cours de développement
- Est officiellement obsolète de PHP 5.5 (juin 2013).
- a été supprimé entièrement à partir de PHP 7.0 (publié décembre 2015)
- cela signifie qu'à partir du 31 déc 2018 il n'existera plus en toute version supportée de PHP. Actuellement, il obtient seulement sécurité mises à jour.
- ne possède pas d'interface OO
- ne supporte pas:
- Non-blocage, les requêtes asynchrones
- Préparées ou des requêtes paramétrées
- procédures stockées
- Déclarations Multiples
- Transactions
- La "nouvelle" méthode d'authentification par mot de passe (par défaut dans MySQL 5.6; 5.7)
- toutes les fonctionnalités de MySQL 5.1
Puisqu'il est déprécié, son utilisation rend votre code moins future preuve.
le manque de soutien pour les déclarations préparées est particulièrement important car elles fournissent une méthode plus claire, moins sujette aux erreurs d'échapper et de citer des données externes que de l'échapper manuellement avec un appel de fonction séparé.
PHP offre trois API différentes pour se connecter à MySQL. Ce sont les mysql
(supprimé à partir de PHP 7), mysqli
, et PDO
extensions.
les fonctions mysql_*
étaient très populaires, mais leur utilisation n'est plus encouragée. L'équipe chargée de la documentation examine la situation en matière de sécurité de la base de données et sensibilise les utilisateurs pour qu'ils s'éloignent de la base de données. l'extension ext/mysql couramment utilisée fait partie de cela (cochez php.internes: autodérision ext/mysql ).
et la dernière équipe de développeurs PHP a pris la décision de générer E_DEPRECATED
erreurs lorsque les utilisateurs se connectent à MySQL, que ce soit par mysql_connect()
, mysql_pconnect()
ou la fonctionnalité de connexion implicite intégrée dans ext/mysql
.
ext/mysql
était officiellement déprécié à partir de PHP 5.5 et a été supprimé à partir de PHP 7 .
voir la boîte rouge?
lorsque vous allez sur n'importe quelle page de manuel de fonction mysql_*
, vous voyez une boîte rouge, expliquant qu'il ne devrait plus être utilisé.
pourquoi
S'éloigner de 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 obtenu que très peu d'ajouts depuis lors tout en gardant la compatibilité avec cette ancienne version qui rend le code un peu plus difficile à maintenir. Caractéristiques manquantes qui ne sont pas prises en charge par ext/mysql
: ( de manuel PHP ).
- procédures stockées (ne peut pas gérer plusieurs jeux de résultats)
- déclarations préparées
- cryptage (SSL)
- Compression
- Plein jeu de caractères de soutien
raison de ne pas utiliser mysql_*
fonction :
- Pas en cours de développement
- supprimé à partir de PHP 7
- ne possède pas d'interface OO
- ne supporte pas les requêtes asynchrones et non-bloquantes
- ne supporte pas les déclarations préparées ou requêtes paramétrisées
- Ne supporte pas les procédures stockées
- ne supporte pas les déclarations multiples
- Ne supporte pas transactions
- ne supporte pas toutes les fonctionnalités de MySQL 5.1
ci-dessus Cité de la réponse de Quentin
le manque de soutien pour les déclarations préparées est particulièrement important car ils fournir une méthode plus claire, moins sujette aux erreurs d'échapper et de citer des données externes que de l'échapper manuellement avec un appel de fonction séparé.
voir la comparaison des extensions SQL .
suppression des avertissements de déprécation
pendant que le code est converti en MySQLi
/ PDO
, E_DEPRECATED
les erreurs peuvent être supprimées par paramètre error_reporting
dans php.ini exclure E_DEPRECATED:
error_reporting = E_ALL ^ E_DEPRECATED
notez que cela masquera aussi autres avertissements de dépréciation , qui, cependant, peut être pour des choses autres que MySQL. ( de manuel PHP )
l'article AOP vs. MySQLi: Que devez-vous utiliser? par dejan Marjanovic vous aidera à choisir.
et une meilleure façon est PDO
, et je suis en train d'écrire un simple tutoriel PDO
.
Un simple et court AOP tutoriel
Q. ma première question était: qu'est-ce que "AOP"?
A. " PDO – PHP Data Objects - couche d'accès à une base de données fournissant une méthode uniforme d'accès à plusieurs bases de données."
Connexion à MySQL
avec la fonction mysql_*
ou on peut le dire à l'ancienne (déprécié en PHP 5.5 et plus haut)
$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);
avec PDO
: Tout ce que vous devez faire est créer un nouvel objet PDO
. Le constructeur accepte les paramètres pour spécifier la source de la base de données PDO
's le constructeur prend la plupart du temps quatre paramètres qui sont DSN
(nom de la source de données) et optionnellement username
, password
.
ici je pense que vous êtes familier avec tous sauf DSN
; c'est nouveau dans PDO
. Un DSN
est essentiellement une chaîne d'options qui disent PDO
quel pilote utiliser, et la connexion détail. Pour de plus amples renseignements, veuillez consulter AOP MySQL DSN .
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');
Note: vous pouvez également utiliser charset=UTF-8
, mais parfois il provoque une erreur, il est donc préférable d'utiliser utf8
.
S'il y a une erreur de connexion, il lancera un PDOException
objet qui peut être attrapé pour manipuler Exception
plus loin.
bonne lecture : Connexions et la gestion de la Connexion ¶
vous pouvez également passer dans plusieurs options de pilote comme un tableau au quatrième paramètre. Je recommande de passer le paramètre qui met PDO
en mode exception. Parce que certains pilotes PDO
ne supportent pas les déclarations natives préparées, donc PDO
effectue l'émulation de la préparation. Il vous permet également d'activer manuellement cette émulation. Pour utiliser les instructions natives préparées du côté du serveur, vous devrait explicitement le définir false
.
l'autre est d'éteindre préparer l'émulation qui est activé dans le pilote MySQL
par défaut, mais préparer l'émulation doit être éteint pour utiliser PDO
en toute sécurité.
j'expliquerai plus tard pourquoi préparer l'émulation devrait être éteint. Pour trouver la raison s'il vous plaît vérifier ce post .
Ce n'est utilisable que si vous utilisez une ancienne version de MySQL
que je ne recommande pas.
ci-dessous est un exemple de la façon dont vous pouvez le faire:
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8',
'username',
'password',
array(PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
pouvons-nous définir des attributs après la construction D'une AOP?
Oui , nous pouvons également définir quelques attributs après la construction de L'AOP avec la méthode setAttribute
:
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8',
'username',
'password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
Traitement Des Erreurs
le traitement des erreurs est beaucoup plus facile dans PDO
que dans mysql_*
.
une pratique courante lors de l'utilisation de mysql_*
est:
//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));
OR die()
n'est pas une bonne façon de gérer l'erreur, puisque nous ne pouvons pas gérer la chose dans die
. Il ne fera que terminer le script brusquement et ensuite faire écho à l'erreur à l'écran que vous ne voulez généralement pas montrer à vos utilisateurs finaux, et laisser les pirates sanglants découvrir votre schéma. Alternativement, les valeurs de retour des fonctions mysql_*
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 avec PDO
doit être emballé dans un bloc try
- catch
. Nous pouvons forcer PDO
dans l'un des trois modes d'erreur en définissant le mode d'erreur d'attribut. Trois modes de traitement des erreurs sont présentés ci-dessous.
-
PDO::ERRMODE_SILENT
. Il s'agit simplement de définir les codes d'erreur et agit à peu près de la même façon quemysql_*
où vous devez vérifier chaque résultat et puis regarder$db->errorInfo();
pour obtenir les détails de l'erreur. -
PDO::ERRMODE_WARNING
LeverE_WARNING
. (Avertissements d'exécution (erreurs non fatales). L'exécution du script n'est pas interrompue.) -
PDO::ERRMODE_EXCEPTION
: Lancer des exceptions. Il s'agit d'une erreur soulevée par AOP. Vous ne devriez pas jeter unPDOException
à partir de votre propre code. Voir Exceptions pour plus d'informations sur les exceptions en PHP. Il agit commeor die(mysql_error());
, quand il n'est pas pris. Mais contrairement àor die()
, lePDOException
peut être attrapé et manipulé avec élégance si vous choisissez de le faire.
bonne lecture :
Comme:
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
et vous pouvez l'envelopper dans try
- catch
, comme ci-dessous:
try {
//Connect as appropriate as above
$db->query('hi'); //Invalid query!
}
catch (PDOException $ex) {
echo "An Error occured!"; //User friendly message/message you want to show to user
some_logging_function($ex->getMessage());
}
vous n'avez pas à manipuler avec try
- catch
en ce moment. Vous pouvez l'attraper à tout moment approprié, mais je vous recommande fortement de utilisez try
- catch
. Aussi, il peut faire plus de sens de le saisir à l'extérieur de la fonction qui appelle le PDO
stuff:
function data_fun($db) {
$stmt = $db->query("SELECT * FROM table");
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
//Then later
try {
data_fun($db);
}
catch(PDOException $ex) {
//Here you can handle error and show message/perform action you want.
}
Aussi, vous pouvez gérer par or die()
ou nous pouvons dire comme mysql_*
, mais il sera vraiment varié. Vous pouvez masquer les messages d'erreur dangereux en production en tournant display_errors off
et en lisant simplement votre journal d'erreurs.
maintenant, après avoir lu toutes les choses ci-dessus, vous êtes probablement penser: qu'est-ce que le diable est que quand je veux juste commencer à penchant simple SELECT
, INSERT
, UPDATE
, ou DELETE
déclarations? Ne vous inquiétez pas, nous y voilà:
Sélectionner Des Données
donc ce que vous faites dans mysql_*
est:
<?php
$result = mysql_query('SELECT * from table') or die(mysql_error());
$num_rows = mysql_num_rows($result);
while($row = mysql_fetch_assoc($result)) {
echo $row['field1'];
}
maintenant dans PDO
, vous pouvez faire comme:
<?php
$stmt = $db->query('SELECT * FROM table');
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo $row['field1'];
}
ou
<?php
$stmt = $db->query('SELECT * FROM table');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
//Use $results
Note : si vous utilisez la méthode comme ci-dessous ( query()
), cette méthode retourne un objet PDOStatement
. Donc, si vous voulez récupérer le résultat, l'utiliser comme ci-dessus.
<?php
foreach($db->query('SELECT * FROM table') as $row) {
echo $row['field1'];
}
dans les données AOP, il est obtenu via le ->fetch()
, une méthode de votre poignée de déclaration. Avant d'appeler fetch, la meilleure approche serait de dire à AOP comment vous voulez les données pour être récupérée. Dans la section ci-dessous, j'explique ceci.
Fetch Modes
noter l'utilisation de PDO::FETCH_ASSOC
dans le code fetch()
et fetchAll()
ci-dessus. Cela dit PDO
pour retourner les lignes comme un tableau associatif avec les noms de champ comme clé. Il y a aussi beaucoup d'autres modes fetch que je vais expliquer un par un.
tout d'abord, je vous explique comment sélectionner le mode fetch:
$stmt->fetch(PDO::FETCH_ASSOC)
dans ce qui précède, j'ai utilisé fetch()
. Vous pouvez également utiliser:
-
PDOStatement::fetchAll()
- renvoie un tableau contenant toutes les lignes de résultat -
PDOStatement::fetchColumn()
- renvoie une colonne simple de la ligne suivante d'un jeu de résultats -
PDOStatement::fetchObject()
- Récupère la ligne suivante et retourne un objet. -
PDOStatement::setFetchMode()
- définissez le mode fetch par défaut pour cette déclaration
Maintenant, je viens la chercher à la mode:
-
PDO::FETCH_ASSOC
: retourne un tableau indexé par nom de colonne comme retourné dans votre jeu de résultats -
PDO::FETCH_BOTH
(par défaut): renvoie un tableau indexé à la fois par le nom de la colonne et par le numéro de colonne 0 Tel qu'il est retourné dans votre jeu de résultats.
il y a encore plus de choix! Lisez à propos d'eux tous dans PDOStatement
Fetch documentation. .
Obtenir le nombre de lignes :
au lieu d'utiliser mysql_num_rows
pour obtenir le nombre de lignes retournées, vous pouvez obtenir un PDOStatement
et faire rowCount()
, comme:
<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';
ID
<?php
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");
$insertId = $db->lastInsertId();
insérer et mettre à jour ou supprimer les énoncés
ce que nous faisons dans mysql_*
fonction est:
<?php
$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
echo mysql_affected_rows($result);
et en AOP, cette même chose peut être faite par:
<?php
$affected_rows = $db->exec("UPDATE table SET field='value'");
echo $affected_rows;
dans la requête ci-dessus PDO::exec
exécuter un SQL instruction et renvoie le nombre de lignes affectées.
d'Insertion et de suppression seront traités plus tard.
la méthode ci-dessus n'est utile que lorsque vous n'utilisez pas la variable dans la requête. Mais quand vous avez besoin d'utiliser une variable dans une requête, ne jamais essayer comme ci-dessus et là pour déclaration préparée ou déclaration paramétrée est.
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 seulement les données au serveur.
typique le flux de travail de l'utilisation d'une déclaration préparée est comme suit ( Cité de Wikipedia trois 3 points ):
-
préparer : le modèle de déclaration est créé par l'application et envoyé au système de gestion de base de données (SGBD). Certaines valeurs ne sont pas précisées, appelées paramètres, espaces ou variables de liaison (étiquetées
?
ci-dessous):INSERT INTO PRODUCT (name, price) VALUES (?, ?)
-
le SGBD analyse, compile et effectue l'optimisation de la requête sur le modèle d'énoncé, et stocke le résultat sans l'exécuter.
- Execute : à un moment ultérieur, l'application fournit (ou lie) des valeurs pour les paramètres, et le SGBD exécute l'instruction (retournant éventuellement un résultat). L'application peut exécuter l'instruction autant de fois qu'il veut avec des valeurs différentes. Dans cet exemple, il peut fournir "Bread" pour le premier paramètre et
1.00
pour le second.
vous pouvez utiliser une déclaration préparée en incluant des espaces réservés dans votre SQL. Il y a essentiellement trois uns sans placeholders (n'essayez pas ceci avec la variable its au-dessus d'un), un avec des placeholders non nommés, et un avec des placeholders nommés.
Q. alors maintenant, ce qui sont nommés les espaces réservés et comment les utiliser?
A. marqueurs 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 position/de l'ordre de la valeur dans le nom de place de titulaire:
$stmt->bindParam(':bla', $bla);
bindParam(parameter,variable,data_type,length,driver_options)
vous pouvez également lier en utilisant un tableau d'exécution aussi bien:
<?php
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
une autre belle caractéristique pour OOP
les amis est que les titulaires nommés ont la capacité 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:
class person {
public $name;
public $add;
function __construct($a,$b) {
$this->name = $a;
$this->add = $b;
}
}
$demo = new person('john','29 bla district');
$stmt = $db->prepare("INSERT INTO table (name, add) value (:name, :add)");
$stmt->execute((array)$demo);
Q. maintenant, que sont les espaces réservés sans nom et comment les utiliser?
A. prenons un exemple:
<?php
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->bindValue(1, $name, PDO::PARAM_STR);
$stmt->bindValue(2, $add, PDO::PARAM_STR);
$stmt->execute();
et
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->execute(array('john', '29 bla district'));
ci-dessus, vous pouvez voir ces ?
au lieu d'un nom comme un nom de marque. Dans le premier exemple, nous assignons des variables aux différents paramètres ( $stmt->bindValue(1, $name, PDO::PARAM_STR);
). Ensuite, nous attribuer des valeurs à ces espaces et exécuter l'instruction. Dans le deuxième exemple, le premier élément de tableau va au premier ?
et le second au second ?
.
NOTE : dans sans nom nous devons prendre soin de l'ordre approprié des éléments dans le tableau que nous passons à la méthode PDOStatement::execute()
.
SELECT
, INSERT
, UPDATE
, DELETE
les requêtes préparées
-
SELECT
:$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name"); $stmt->execute(array(':name' => $name, ':id' => $id)); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-
INSERT
:$stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)"); $stmt->execute(array(':field1' => $field1, ':field2' => $field2)); $affected_rows = $stmt->rowCount();
-
DELETE
:$stmt = $db->prepare("DELETE FROM table WHERE id=:id"); $stmt->bindValue(':id', $id, PDO::PARAM_STR); $stmt->execute(); $affected_rows = $stmt->rowCount();
-
UPDATE
:$stmt = $db->prepare("UPDATE table SET name=? WHERE id=?"); $stmt->execute(array($name, $id)); $affected_rows = $stmt->rowCount();
NOTE:
cependant PDO
et / ou MySQLi
sont pas complètement sûr. Cochez la réponse les déclarations préparées par AOP sont-elles suffisantes pour prévenir L'injection de SQL? par ircmaxell . Aussi, je cite une partie de sa réponse:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES GBK');
$stmt = $pdo->prepare("SELECT * FROM test WHERE name = ? LIMIT 1");
$stmt->execute(array(chr(0xbf) . chr(0x27) . " OR 1=1 /*"));
tout d'abord, commençons par le commentaire standard que nous donnons à tout le monde:
s'il vous Plaît, n'utilisez pas
mysql_*
les fonctions dans le nouveau code . Ils ne sont plus maintenus et sont officiellement dépréciés . Voir la boîte rouge ? En savoir plus sur déclarations préparées à la place, et utiliser AOP ou MySQLi - cet article vous aidera à décider quel. Si vous choisissez AOP, voici un bon tutoriel .
voyons cela, phrase par phrase, et expliquons-nous:
-
ils ne sont plus entretenus, et sont officiellement dépréciés
cela signifie que la communauté PHP abandonne progressivement le soutien à 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 briser votre code dans le (pas si) l'avenir lointain.
nouveau! - ext / mysql est maintenant officiellement déprécié à partir de PHP 5.5!
Nouveau! ext / mysql a été supprimé en PHP 7 .
-
au lieu de cela, vous devriez apprendre des déclarations préparées
mysql_*
extension ne prend pas en charge préparées , qui est (entre autres choses) un moyen très efficace de contre-mesure contre les SQL Injection . 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 exécuter toute requête possible sur votre base de données.pour plus d'informations, voir Comment puis-je empêcher L'injection SQL en PHP?
-
voir la boîte rouge?
lorsque vous allez à n'importe quelle page de manuel de fonction
mysql
, vous voyez une boîte rouge, expliquant qu'il ne devrait plus être utilisé. -
utiliser AOP ou MySQLi
il y a des alternatives meilleures, plus robustes et bien construites, PDO-PHP Database Object , qui offre une approche complète OOP à l'interaction de base de données, et MySQLi , qui est une amélioration spécifique mysl.
facilité d'utilisation
les raisons analytiques et synthétiques ont déjà été mentionnées. Pour les nouveaux venus il y a une incitation plus significative à cesser d'utiliser les fonctions mysql_ datées.
base de données contemporaine les API sont simplement plus faciles à utiliser.
ce sont surtout les " bound parameters qui peuvent simplifier le code. Et avec excellent tutoriels (voir ci-dessus) la transition vers AOP n'est pas trop ardue.
réécrire une base de code plus large à la fois prend cependant du temps. Raison d'être de cette solution intermédiaire:
équivalent pdo_* fonctions à la place de mysql_*
utilisant < pdo_mysql.php > vous pouvez passer de l'ancienne mysql_ fonctions avec effort minimal . Il ajoute les enveloppements de fonction pdo_
qui remplacent leurs homologues mysql_
.
-
simplement
include_once(
"pdo_mysql.php"
);
dans chaque script d'invocation qui doit interagir avec la base de données.
-
supprimer le
mysql_
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()
- et ainsi de suite...
-
-
votre code fonctionnera de la même façon et ressemblera encore la plupart du temps à la même chose:
include_once("pdo_mysql.php"); pdo_connect("localhost", "usrABC", "pw1234567"); pdo_select_db("test"); $result = pdo_query("SELECT title, html FROM pages"); while ($row = pdo_fetch_assoc($result)) { print "$row[title] - $row[html]"; }
et voilà.
Votre code est en utilisant AOP.
Maintenant, il est temps de réellement utiliser .
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.
- ajouter les paramètres de fonction délimités par une virgule à
pdo_query()
. - placer des points d'interrogation
?
comme des espaces réservés où les variables étaient auparavant. - se débarrasser de
'
guillemets simples qui incluaient auparavant des valeurs/variables de chaîne.
l'avantage devient plus évident pour le code plus long.
souvent les variables string ne sont pas simplement interpolées en SQL, mais concaténées avec des appels échappés entre les deux.
pdo_query("SELECT id, links, html, title, user, date FROM articles
WHERE title='" . pdo_real_escape_string($title) . "' OR id='".
pdo_real_escape_string($title) . "' AND user <> '" .
pdo_real_escape_string($root) . "' ORDER BY date")
avec ?
placeholders appliqués vous n'avez pas à vous soucier de cela:
pdo_query("SELECT id, links, html, title, user, date FROM articles
WHERE title=? OR id=? AND user<>? ORDER BY date", $title, $id, $root)
rappelez-vous que AOP_* permet toujours soit ou .
Il suffit de ne pas échapper à une variable et lient dans la même requête.
- la fonction de positionnement est fournie par le véritable AOP derrière elle.
- donc aussi autorisé plus tard
:named
des listes de paramètres.
plus important encore, vous pouvez passer $_REQUEST[] variables en toute sécurité derrière toute requête. Lorsque les champs <form>
correspondent exactement à la structure de la base de données, elle est encore plus courte:
pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);
autant de simplicité. Mais revenons à quelques conseils de réécriture et des raisons techniques sur pourquoi vous pouvez vouloir vous débarrasser de et échapper. mysql_
fixer ou enlever une fonction sanitize()
1519890920
une fois que vous avez converti tous les appels vers mysql_
pdo_query
avec params liés, supprimer tous les appels redondants pdo_real_escape_string
.
en particulier, vous devez corriger toute fonction sanitize
ou clean
ou filterThis
ou clean_data
comme annoncé par des tutoriels datés sous une forme ou l'autre:
function sanitize($str) {
return trim(strip_tags(htmlentities(pdo_real_escape_string($str))));
}
le bogue le plus flagrant ici est le manque de documentation. De façon plus significative, l'ordre du filtrage était exactement dans le mauvais ordre.
-
L'ordre Correct aurait été:
stripslashes
comme l'appel interne, puistrim
, ensuitestrip_tags
,htmlentities
pour le contexte de sortie, et enfin_escape_string
comme son application devrait précéder directement L'intersaison SQL. -
mais comme première étape juste se débarrasser de la
_real_escape_string
appel. -
vous devrez peut-être conserver le reste de votre fonction
sanitize()
pour l'instant si votre base de données et votre flux d'application s'attendent à des chaînes HTML-context-safe. Ajouter un commentaire qu'il ne s'applique que HTML échappant désormais. -
Chaîne de valeur/la manipulation est déléguée à des AOP et son des instructions paramétrées.
-
S'il y avait mention de
stripslashes()
dans votre fonction d'assainissement, cela pourrait indiquer niveau supérieur de supervision.-
qui était souvent là pour annuler les dommages (double échappatoire) de l'obsolète
magic_quotes
. Qui est cependant mieux fixé au centre , pas Chaîne par chaîne. -
utilisez l'une des approches userland reversal . Puis supprimer le
stripslashes()
dans la fonctionsanitize
.
note historique sur magic_quotes. cette caractéristique est à juste titre dépréciée. Il est souvent présenté à tort comme un échec sécurité caractéristique cependant. Mais magic_quotes est tout autant un défaut de sécurité que les balles de tennis ont échoué comme source de nutrition. Qui n'était tout simplement pas leur but.
la mise en œuvre originale dans PHP2 / FI l'a introduit explicitement avec juste" les guillemets seront automatiquement échappés rendant plus facile de passer des données de formulaire directement aux requêtes msql ". En particulier, il était accidentellement sûr à utiliser avec mSQL , car celui pris en charge ASCII seulement.
Puis PHP3 / Zend a réintroduit magic_quotes pour MySQL et l'a mal décrit. Mais à l'origine , il était juste un commodité caractéristique , pas l'intention de la sécurité. -
degré de préparation des déclarations diffèrent
quand vous mélangez des variables string dans les requêtes SQL, cela ne devient pas plus compliqué à suivre. C'est aussi un effort étranger pour MySQL de séparer le code et les données à nouveau.
les injections SQL se produisent simplement lorsque les données se retrouvent dans le contexte du code . Un serveur de base de données ne peut pas plus tard spot où PHP a collé à l'origine 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 ne se mélange plus dans les coulisses (sauf avec PDO::EMULATE_PREPARES). Votre base de données reçoit les commandes SQL Non variables et des valeurs variables 1:1.
alors que cette réponse souligne que vous devez vous soucier de la avantages de l'abandon . Il y a parfois aussi un avantage de performance (INSERTs répétés avec des valeurs différentes) en raison de cette séparation visible et technique des données/Codes. mysql_
attention, la fixation des paramètres n'est toujours pas une solution miracle contre les injections all SQL. Il traite l'utilisation la plus courante pour les données/valeurs. Mais ne peut pas whitelist colonne Nom / identificateurs de table, aide avec construction de la clause dynamique, ou juste des listes de valeurs de tableaux simples.
utilisation D'AOP Hybride
ces fonctions d'enrubannage pdo_*
permettent de créer une API de stop-gap conviviale pour le codage. (C'est à peu près ce que MYSQLI
aurait pu être s'il n'y avait pas eu le changement de signature idiosyncratique de la fonction). Ils exposent aussi le vrai AOP la plupart du temps.
La réécriture ne doit pas s'arrêter à l'utilisation des nouveaux noms de fonction pdo_. Vous pourriez un par un transition de chaque pdo_query () dans un appel simple $pdo->prepare ()->execute ().
Il est préférable de commencer à simplifier encore cependant. Par exemple le résultat courant fetching:
$result = pdo_query("SELECT * FROM tbl");
while ($row = pdo_fetch_assoc($result)) {
peut être remplacé par une simple itération d'avant-chaque:
foreach ($result as $row) {
Ou mieux encore, une directe et totale de la matrice de récupération:
$result->fetchAll();
vous obtiendrez des avertissements plus utiles dans la plupart des cas que PDO ou mysql_ habituellement fournir après l'échec des requêtes.
autres options
donc cela visualisait avec un peu de chance raisons pratiques et une voie de chute valable . mysql_
passer à AOP ne suffit pas tout à fait. pdo_query()
n'est aussi qu'une façade.
sauf si vous introduisez également la liaison de paramètre ou pouvez utiliser quelque chose d'autre de la plus jolie API, c'est un interrupteur inutile. J'espère que c'est assez simple pour ne pas décourager les nouveaux. (L'éducation fonctionne généralement mieux que la prohibition.)
bien qu'il se qualifie pour la catégorie la plus simple-chose-qui-pourrait-peut-être-travailler, il est aussi encore code très expérimental. J'ai juste écrit sur le week-end. Il y a cependant une pléthore d'alternatives. Juste google pour PHP database abstraction et parcourir un peu. Il y a toujours eu et il y aura beaucoup d'excellentes bibliothèques pour de telles tâches.
si vous voulez simplifier davantage votre interaction avec la base de données, des mappers comme Paris/Idiorm valent la peine d'être essayés. Tout comme plus personne n'utilise le DOM bland en JavaScript, vous n'avez plus besoin de garder une interface de base de données brute de nos jours.
Le mysql_
fonctions:
- sont périmés - ils ne sont plus entretenus
- ne vous permet pas de vous déplacer facilement à une autre base de données backend
- ne prennent pas en charge les requêtes préparées, d'où
- encourager les programmeurs à utiliser concaténation pour construire des requêtes, conduisant à des vulnérabilités D'injection SQL
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.
Peut-être que je suis trop ignorant, mais je n'ai jamais eu l'occasion d'utiliser des choses comme
- non-blocage, les requêtes asynchrones
- procédures stockées retournant des jeux de résultats multiples
- cryptage (SSL)
- Compression
si vous en avez besoin - ce sont sans doute des raisons techniques pour passer de l'extension mysql vers quelque chose de plus élégant et moderne.
Néanmoins, Il ya aussi quelques questions non techniques, qui peuvent rendre votre expérience un peu plus difficile
- l'utilisation ultérieure de ces fonctions avec les versions modernes de PHP soulèvera avis de niveau déprécié. Ils peuvent simplement être éteints.
- dans un futur lointain, ils peuvent être supprimés de la compilation PHP par défaut. Ce n'est pas grand chose non plus, puisque mydsql ext sera transféré dans PECL et chaque hébergeur sera heureux de compiler PHP avec, car ils ne veulent pas perdre des clients dont les sites ont fonctionné pendant des décennies.
- la forte résistance de Stackoverflow de la communauté. Chaque fois que vous mentionnez ces fonctions honnêtes, on vous dit qu'ils sont strictement tabous.
- étant un utilisateur PHP moyen, il est probable que votre idée d'utiliser ces fonctions est sujette à erreur et fausse. Juste à cause de tous ces nombreux tutoriels et manuels qui vous enseignent la mauvaise façon. Pas les fonctions elles-mêmes - je dois le souligner - mais la façon dont ils sont utilisés.
cette dernière question est problématique.
Mais, à mon avis, la solution proposée n'est pas mieux non plus.
Il me semble trop idéaliste un rêve que tous ces utilisateurs de PHP apprendront comment gérer correctement les requêtes SQL à la fois. Très probablement ils changeraient mysql_* en mysqli_* mécaniquement, laissant l'approche la même . Surtout parce que mysqli rend l'utilisation des déclarations préparées incroyablement douloureuse et gênante.
Sans oublier que natif déclarations préparées ne sont pas assez pour protéger des injections SQL, et ni mysqli ni PDO offre une solution.
donc, au lieu de combattre cette extension honnête, je préférerais combattre les mauvaises pratiques et éduquer les gens de la bonne manière.
il y a aussi des raisons fausses ou non significatives, comme
- ne supporte pas les procédures stockées (nous utilisions
mysql_query("CALL my_proc");
depuis des âges) - Ne prend pas en charge les Transactions (le même que ci-dessus)
- ne supporte pas les déclarations multiples (qui en a besoin?)
- pas en développement actif (et alors? elle affecte vous une manière pratique?)
- N'a pas d'interface OO (en créer une est une question de plusieurs heures)
- ne supporte pas les énoncés préparés ou les requêtes paramétrées
le dernier point est intéressant. Bien que mysql ext ne supporte pas natif déclarations préparées, ils ne sont pas nécessaires pour la sécurité. Nous pouvons facilement simuler des déclarations préparées en utilisant des espaces réservés manipulés manuellement (comme le fait PDO):
function paraQuery()
{
$args = func_get_args();
$query = array_shift($args);
$query = str_replace("%s","'%s'",$query);
foreach ($args as $key => $val)
{
$args[$key] = mysql_real_escape_string($val);
}
$query = vsprintf($query, $args);
$result = mysql_query($query);
if (!$result)
{
throw new Exception(mysql_error()." [$query]");
}
return $result;
}
$query = "SELECT * FROM table where a=%s AND b LIKE %s LIMIT %d";
$result = paraQuery($query, $a, "%$b%", $limit);
voilà , tout est paramétré et sûr.
Mais bon, si vous n'aimez pas la boîte rouge dans le manuel, un problème de le choix se pose: mysqli ou AOP?
Eh bien, la réponse serait la suivante:
- si vous comprenez la nécessité d'utiliser une couche d'abstraction de base de données et de chercher une API pour en créer une, mysqli est un très bon choix, car il supporte en effet de nombreuses fonctionnalités spécifiques à mysql.
-
si, comme la grande majorité des gens de PHP, vous utilisez des appels API bruts directement dans le code d'application (qui est essentiellement une mauvaise pratique) - PDO est le seul choix , car cette extension prétend être non seulement API, mais plutôt un semi-DAL, encore incomplet, mais offre de nombreuses fonctionnalités importantes, avec deux d'entre eux rend PDO critique distinguée de mysqli:
- à la différence de mysqli, PDO peut lier les espaces par la valeur , ce qui rend les requêtes dynamiquement construites possibles sans plusieurs écrans de un code assez confus.
- contrairement à mysqli, PDO peut toujours retourner le résultat de la requête dans un simple tableau habituel, alors que mysqli ne peut le faire que sur les installations mysqlnd.
donc, si vous êtes un utilisateur PHP moyen et que vous voulez vous épargner une tonne de maux de tête lors de l'utilisation de déclarations natives préparées, PDO - encore - est le seul choix.
Toutefois, le PDO n'est pas non plus une solution miracle et connaît des difficultés.
Ainsi, j'ai écrit des solutions pour tous les pièges communs et les cas complexes dans le AOP tag wiki
néanmoins, tout le monde parle d'extensions toujours manquer le 2 faits importants à propos de Mysqli et AOP:
-
Préparée n'est pas une balle d'argent . Il y a des identificateurs dynamiques qui ne peuvent pas être relié à l'aide de déclarations préparées à l'avance. Il y a des requêtes dynamiques avec un nombre inconnu de paramètres qui rendent la construction d'une requête difficile.
-
ni les fonctions mysqli_* ni les fonctions PDO n'auraient dû apparaître dans le code d'application.
Il devrait y avoir une couche d'abstraction entre eux et le code d'application, qui fera tout le sale travail de liaison, boucle, erreur la manipulation, etc. a l'intérieur, faire sécher et nettoyer le code d'application. Spécialement pour les cas complexes comme la construction de requête dynamique.
donc, passer à AOP ou mysqli n'est pas suffisant. Il faut utiliser un ORM, ou un constructeur de requêtes, ou n'importe quelle classe d'abstraction de base de données au lieu d'appeler des fonctions D'API brutes dans leur code.
Et 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 soit déprécié et ensuite facilement réécrire votre classe d'abstraction à un autre moteur, ayant tout le code d'application intact.
voici quelques exemples basés sur ma classe safemysql pour montrer comment une telle classe d'abstraction devrait être:
$city_ids = array(1,2,3);
$cities = $db->getCol("SELECT name FROM cities WHERE is IN(?a)", $city_ids);
comparez cette seule ligne avec montant de code dont vous aurez besoin avec AOP .
Puis comparer avec montant fou de code vous aurez besoin avec des déclarations brutes Mysqli préparé.
Notez que la gestion des erreurs, le profilage, la journalisation des requêtes déjà intégrée et en cours d'exécution.
$insert = array('name' => 'John', 'surname' => "O'Hara");
$db->query("INSERT INTO users SET ?u", $insert);
comparez-le avec les insertions AOP habituelles, lorsque chaque nom de champ est répété six à dix fois - dans tous ces nombreux noms d'espaces, de reliures et de requêtes définition.
autre exemple:
$data = $db->getAll("SELECT * FROM goods ORDER BY ?n", $_GET['order']);
vous pouvez à peine trouver un exemple pour AOP pour traiter un tel cas pratique.
Et ce sera trop verbeux et probablement dangereux.
donc, une fois de plus - ce n'est pas seulement pilote brut devrait être votre préoccupation mais Classe d'abstraction, utile non seulement pour des exemples stupides du manuel du débutant mais pour résoudre tous les problèmes de la vie réelle.
il y a plusieurs raisons, mais peut-être la plus importante est que ces fonctions encouragent des pratiques de programmation non sûres parce qu'elles ne supportent pas les déclarations préparées. Les déclarations préparées aident à prévenir les attaques par injection SQL.
lorsque vous utilisez les fonctions mysql_*
, vous devez vous rappeler d'exécuter les paramètres fournis par l'utilisateur par mysql_real_escape_string()
. Si vous oubliez dans un seul endroit ou si vous vous trouvez à échapper seulement une partie de l'entrée, votre base de données peut être soumis à attaque.
L'utilisation d'énoncés préparés dans PDO
ou mysqli
le rendra de sorte que ce genre d'erreurs de programmation sont plus difficiles à faire.
parce que (entre autres raisons) il est beaucoup plus difficile de s'assurer que les données d'entrée sont épurées. Si vous utilisez des requêtes paramétrées, comme on le fait avec PDO ou mysqli, vous pouvez totalement é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, de sorte que quelque chose comme ce méchant bougre peut supprimer une table entière.
si l'on devait utiliser L'AOP de mysqli, le nom d'utilisateur finirait par être "enhzflep); drop table users"
.
voir bobby-tables.com .
cette réponse est écrite pour montrer à quel point il est trivial de contourner le code de validation de L'utilisateur de PHP mal écrit, comment (et en utilisant quoi) ces attaques fonctionnent et comment remplacer les anciennes fonctions MySQL par une déclaration préparée sécurisée-et fondamentalement, pourquoi les utilisateurs de StackOverflow (probablement avec beaucoup de rep) aboyent sur les nouveaux utilisateurs qui posent des questions pour améliorer leur code.
tout d'abord, s'il vous plaît n'hésitez pas à créer cette base de données mysql test (j'ai appelé mine prep):
mysql> create table users(
-> id int(2) primary key auto_increment,
-> userid tinytext,
-> pass tinytext);
Query OK, 0 rows affected (0.05 sec)
mysql> insert into users values(null, 'Fluffeh', 'mypass');
Query OK, 1 row affected (0.04 sec)
mysql> create user 'prepared'@'localhost' identified by 'example';
Query OK, 0 rows affected (0.01 sec)
mysql> grant all privileges on prep.* to 'prepared'@'localhost' with grant option;
Query OK, 0 rows affected (0.00 sec)
avec cela fait, nous pouvons passer à notre code PHP.
laisse supposer que le script suivant est le processus de vérification pour un administrateur sur un site web (simplifié mais efficace si vous le copiez et l'utilisez pour des tests):
<?php
if(!empty($_POST['user']))
{
$user=$_POST['user'];
}
else
{
$user='bob';
}
if(!empty($_POST['pass']))
{
$pass=$_POST['pass'];
}
else
{
$pass='bob';
}
$database='prep';
$link=mysql_connect('localhost', 'prepared', 'example');
mysql_select_db($database) or die( "Unable to select database");
$sql="select id, userid, pass from users where userid='$user' and pass='$pass'";
//echo $sql."<br><br>";
$result=mysql_query($sql);
$isAdmin=false;
while ($row = mysql_fetch_assoc($result)) {
echo "My id is ".$row['id']." and my username is ".$row['userid']." and lastly, my password is ".$row['pass']."<br>";
$isAdmin=true;
// We have correctly matched the Username and Password
// Lets give this person full access
}
if($isAdmin)
{
echo "The check passed. We have a verified admin!<br>";
}
else
{
echo "You could not be verified. Please try again...<br>";
}
mysql_close($link);
?>
<form name="exploited" method='post'>
User: <input type='text' name='user'><br>
Pass: <input type='text' name='pass'><br>
<input type='submit'>
</form>
semble légitime à première vue.
, L'utilisateur doit entrer un login et un mot de passe, non?
brillant, ne pas entrer dans le suivant:
user: bob
pass: somePass
et soumettez-le.
la sortie est la suivante:
You could not be verified. Please try again...
Super! En travaillant comme prévu, essayons maintenant le nom d'utilisateur et le mot de passe:
user: Fluffeh
pass: mypass
incroyable! Hi-fives all round, le code correctement vérifié un administrateur. C'est parfait!
enfin, pas vraiment. Disons que l'utilisateur est un astucieux petit personne. Permet de dire que la personne est moi.
Inscrivez ce qui suit:
user: bob
pass: n' or 1=1 or 'm=m
et la sortie est:
The check passed. We have a verified admin!
félicitations, vous venez de me permettre d'entrer dans votre section admins super-protégé seulement avec moi saisissant 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 lancez ce code PHP - qui à première vue semble vraiment vérifier le nom d'utilisateur et le mot de passe assez bien.
Donc, en réponse, C'est pourquoi on vous crie dessus.
donc, regardons ce qui s'est mal passé, et pourquoi je viens d'entrer dans votre super-admin-only-bat-cave. J'ai supposé que vous n'étiez pas prudent avec vos entrées et je les ai passées directement à la base de données. J'ai construit l'entrée de manière à changer la requête que vous étiez en train d'exécuter. Donc, ce qui était censé être, et qu'a-t-elle?
select id, userid, pass from users where userid='$user' and pass='$pass'
C'est la requête, mais quand nous remplaçons les variables par les entrées réelles que nous avons utilisées, nous obtenons ce qui suit:
select id, userid, pass from users where userid='bob' and pass='n' or 1=1 or 'm=m'
voyez comment j'ai construit mon "mot de passe" de sorte qu'il fermerait d'abord la citation simple autour du mot de passe, puis introduire une comparaison complètement nouvelle? Puis juste pour la sécurité, j'ai ajouté une autre "chaîne" de sorte que la citation simple serait fermée comme prévu dans le code que nous avions à l'origine.
Cependant, ce n'est pas à propos des gens qui vous crient dessus, il s'agit de vous montrer comment sécuriser votre code.
OK, alors qu'est-ce qui s'est mal passé, et comment pouvons-nous le réparer?
c'est une attaque classique par injection SQL. Le moyen le plus simple pour cette question. Sur l'échelle des vecteurs d'attaque, c'est un enfant qui attaque un tank - et gagne.
alors, comment protéger votre section administrative sacrée et la rendre agréable et sûre? La première chose à faire sera de arrêtez d'utiliser ces fonctions mysql_*
vraiment anciennes et obsolètes. Je sais, vous avez suivi un tutoriel vous l'avez trouvé en ligne et il fonctionne, mais il est vieux, il est obsolète et en l'espace de quelques minutes, j'ai juste cassé passé sans casser une sueur.
Maintenant, vous avez les meilleures options d'utiliser mysqli_ ou AOP . Je suis personnellement un grand fan de PDO, donc je vais utiliser PDO dans le reste de cette réponse. Il y a les pros et les con, mais personnellement, je trouve que les pros l'emportent de loin sur les con. Il est portable à travers plusieurs moteurs de base de données - que vous utilisiez MySQL ou Oracle ou n'importe quoi d'autre - juste en changeant la chaîne de connexion, il a toutes les fonctionnalités de fantaisie que nous voulons utiliser et il est agréable et propre. J'aime nettoyer.
maintenant, regardons à nouveau ce code, cette fois écrit en utilisant un objet AOP:
<?php
if(!empty($_POST['user']))
{
$user=$_POST['user'];
}
else
{
$user='bob';
}
if(!empty($_POST['pass']))
{
$pass=$_POST['pass'];
}
else
{
$pass='bob';
}
$isAdmin=false;
$database='prep';
$pdo=new PDO ('mysql:host=localhost;dbname=prep', 'prepared', 'example');
$sql="select id, userid, pass from users where userid=:user and pass=:password";
$myPDO = $pdo->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
if($myPDO->execute(array(':user' => $user, ':password' => $pass)))
{
while($row=$myPDO->fetch(PDO::FETCH_ASSOC))
{
echo "My id is ".$row['id']." and my username is ".$row['userid']." and lastly, my password is ".$row['pass']."<br>";
$isAdmin=true;
// We have correctly matched the Username and Password
// Lets give this person full access
}
}
if($isAdmin)
{
echo "The check passed. We have a verified admin!<br>";
}
else
{
echo "You could not be verified. Please try again...<br>";
}
?>
<form name="exploited" method='post'>
User: <input type='text' name='user'><br>
Pass: <input type='text' name='pass'><br>
<input type='submit'>
</form>
les principales différences sont qu'il n'y a plus 1519140920" des fonctions. Tout est fait via un objet AOP, deuxièmement, il utilise une déclaration préparée. Qu'est-ce qu'une déclaration préétablie vous demandez? C'est une façon de dire à la base de données à l'avance de l'exécution d'une requête, la requête est que nous allons lancer. Dans ce cas, nous disons à la base de données: "Salut, je vais lancer une instruction select voulant id, userid et pass des utilisateurs de la table où l'userid est une variable et le pass est aussi une variable.".
Ensuite, dans l'instruction execute, nous passons à la base de données un tableau avec toutes les variables qu'elle attend maintenant.
Les résultats sont fantastiques. Essayons encore une fois les combinaisons nom d'utilisateur et mot de passe d'avant:
user: bob
pass: somePass
L'utilisateur n'a pas été vérifié. Impressionnant.
Que Diriez-vous de:
user: Fluffeh
pass: mypass
Oh, je viens de m'exciter un peu, ça a marché: le chèque est passé. Nous avons un administrateur vérifié!
maintenant, essayons les données qu'un chap intelligent entrerait pour essayer de passer notre petit système de vérification:
user: bob
pass: n' or 1=1 or 'm=m
cette fois, nous obtenons ce qui suit:
You could not be verified. Please try again...
c'est pourquoi on vous crie dessus quand vous postez des questions - c'est parce que les gens peuvent voir que votre code peut être contourné sans même essayer. S'il vous plaît, utilisez cette question et la réponse pour améliorer votre code, pour le rendre plus sûr et d'utiliser des fonctions qui sont actuel.
enfin, ce n'est pas pour dire que c'est du code parfait. Il y a beaucoup plus de 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 sensetive dans la base de données, vous ne le stockez pas dans le texte simple, ont plusieurs niveaux de vérification - mais vraiment, si vous venez de changer votre vieux code sujet à l'injection à ce, vous serez bien sur le chemin d'écrire du bon code - et le fait que vous avez obtenu ce encore en lisant me donne un sentiment d'espoir que vous ne serez pas seulement mettre en œuvre ce type de code lors de l'écriture de vos sites Web et applications, mais que vous pourriez sortir et rechercher ces autres choses que je viens de mentionner - et plus encore. Écrivez le meilleur code que vous pouvez, pas le code le plus basique qui fonctionne à peine.
L'extension MySQL est la plus ancienne des trois et était la façon originale que les développeurs utilisés pour communiquer avec MySQL. Cette extension est maintenant déprécié en faveur de l'autre deux alternatives en raison des améliorations apportées dans les nouvelles versions de PHP et MySQL.
-
MySQLi est l'extension "améliorée" pour travailler avec MySQL les bases de données. Il tire parti des fonctionnalités qui sont disponibles dans les nouvelles versions du serveur MySQL, expose à la fois une fonction orientée et une interface orientée objet pour le développeur et fait quelques autres choses astucieuses.
-
PDO offre une API qui consolide la plupart des fonctionnalités qui étaient auparavant réparties dans les principales extensions D'accès aux bases de données, c.-à-d. MySQL, PostgreSQL, SQLite, MSSQL, etc. L'interface expose les objets de haut niveau pour le programmeur de travailler avec des connexions de base de données, des requêtes et des ensembles de résultats, et les pilotes de bas niveau effectuent la communication et la manipulation de ressources avec le serveur de base de données. Beaucoup de discussions et de travail est en cours dans AOP et il est considéré comme la méthode appropriée de travailler avec les bases de données dans le code moderne et professionnel.
je trouve les réponses ci-dessus vraiment longues, donc pour résumer:
l'extension mysqli a un certain nombre de avantages, les principales améliorations par rapport à l'extension mysql étant:
- interface orientée Objet
- Support pour les Instructions Préparées
- prise en charge de Plusieurs États
- Soutien pour les Transactions
- Enhanced capacités de débogage
- Embedded server support
Source: MySQLi "vue d'ensemble de 1519220920"
comme expliqué dans les réponses ci-dessus, les alternatives à mysql sont mysqli et PDO (objets de données PHP).
- L'API supporte les déclarations préparées côté serveur: supportées par MYSQLi et PDO Prise en charge de L'API
- déclarations préparées par le client: prises en charge uniquement par PDO
- API prend en charge les procédures stockées: à la fois MySQLi et PDO
- API prend en charge les déclarations multiples et toutes les fonctionnalités MySQL 4.1+ - pris en charge par MySQLi et la plupart du temps aussi par PDO
MySQLi et PDO ont été introduits dans PHP 5.0, alors que MySQL a été introduit avant PHP 3.0. Un point à noter est que MySQL est inclus dans PHP5.x bien que déprécié dans les versions ultérieures.
il est possible de définir presque toutes les fonctions mysql_*
en utilisant mysqli ou PDO. Il suffit de les inclure sur votre ancienne application PHP, et cela fonctionnera sur PHP7. Ma solution ici .
<?php
define('MYSQL_LINK', 'dbl');
$GLOBALS[MYSQL_LINK] = null;
function mysql_link($link=null) {
return ($link === null) ? $GLOBALS[MYSQL_LINK] : $link;
}
function mysql_connect($host, $user, $pass) {
$GLOBALS[MYSQL_LINK] = mysqli_connect($host, $user, $pass);
return $GLOBALS[MYSQL_LINK];
}
function mysql_pconnect($host, $user, $pass) {
return mysql_connect($host, $user, $pass);
}
function mysql_select_db($db, $link=null) {
$link = mysql_link($link);
return mysqli_select_db($link, $db);
}
function mysql_close($link=null) {
$link = mysql_link($link);
return mysqli_close($link);
}
function mysql_error($link=null) {
$link = mysql_link($link);
return mysqli_error($link);
}
function mysql_errno($link=null) {
$link = mysql_link($link);
return mysqli_errno($link);
}
function mysql_ping($link=null) {
$link = mysql_link($link);
return mysqli_ping($link);
}
function mysql_stat($link=null) {
$link = mysql_link($link);
return mysqli_stat($link);
}
function mysql_affected_rows($link=null) {
$link = mysql_link($link);
return mysqli_affected_rows($link);
}
function mysql_client_encoding($link=null) {
$link = mysql_link($link);
return mysqli_character_set_name($link);
}
function mysql_thread_id($link=null) {
$link = mysql_link($link);
return mysqli_thread_id($link);
}
function mysql_escape_string($string) {
return mysql_real_escape_string($string);
}
function mysql_real_escape_string($string, $link=null) {
$link = mysql_link($link);
return mysqli_real_escape_string($link, $string);
}
function mysql_query($sql, $link=null) {
$link = mysql_link($link);
return mysqli_query($link, $sql);
}
function mysql_unbuffered_query($sql, $link=null) {
$link = mysql_link($link);
return mysqli_query($link, $sql, MYSQLI_USE_RESULT);
}
function mysql_set_charset($charset, $link=null){
$link = mysql_link($link);
return mysqli_set_charset($link, $charset);
}
function mysql_get_host_info($link=null) {
$link = mysql_link($link);
return mysqli_get_host_info($link);
}
function mysql_get_proto_info($link=null) {
$link = mysql_link($link);
return mysqli_get_proto_info($link);
}
function mysql_get_server_info($link=null) {
$link = mysql_link($link);
return mysqli_get_server_info($link);
}
function mysql_info($link=null) {
$link = mysql_link($link);
return mysqli_info($link);
}
function mysql_get_client_info() {
$link = mysql_link();
return mysqli_get_client_info($link);
}
function mysql_create_db($db, $link=null) {
$link = mysql_link($link);
$db = str_replace('`', '', mysqli_real_escape_string($link, $db));
return mysqli_query($link, "CREATE DATABASE `$db`");
}
function mysql_drop_db($db, $link=null) {
$link = mysql_link($link);
$db = str_replace('`', '', mysqli_real_escape_string($link, $db));
return mysqli_query($link, "DROP DATABASE `$db`");
}
function mysql_list_dbs($link=null) {
$link = mysql_link($link);
return mysqli_query($link, "SHOW DATABASES");
}
function mysql_list_fields($db, $table, $link=null) {
$link = mysql_link($link);
$db = str_replace('`', '', mysqli_real_escape_string($link, $db));
$table = str_replace('`', '', mysqli_real_escape_string($link, $table));
return mysqli_query($link, "SHOW COLUMNS FROM `$db`.`$table`");
}
function mysql_list_tables($db, $link=null) {
$link = mysql_link($link);
$db = str_replace('`', '', mysqli_real_escape_string($link, $db));
return mysqli_query($link, "SHOW TABLES FROM `$db`");
}
function mysql_db_query($db, $sql, $link=null) {
$link = mysql_link($link);
mysqli_select_db($link, $db);
return mysqli_query($link, $sql);
}
function mysql_fetch_row($qlink) {
return mysqli_fetch_row($qlink);
}
function mysql_fetch_assoc($qlink) {
return mysqli_fetch_assoc($qlink);
}
function mysql_fetch_array($qlink, $result=MYSQLI_BOTH) {
return mysqli_fetch_array($qlink, $result);
}
function mysql_fetch_lengths($qlink) {
return mysqli_fetch_lengths($qlink);
}
function mysql_insert_id($qlink) {
return mysqli_insert_id($qlink);
}
function mysql_num_rows($qlink) {
return mysqli_num_rows($qlink);
}
function mysql_num_fields($qlink) {
return mysqli_num_fields($qlink);
}
function mysql_data_seek($qlink, $row) {
return mysqli_data_seek($qlink, $row);
}
function mysql_field_seek($qlink, $offset) {
return mysqli_field_seek($qlink, $offset);
}
function mysql_fetch_object($qlink, $class="stdClass", array $params=null) {
return ($params === null)
? mysqli_fetch_object($qlink, $class)
: mysqli_fetch_object($qlink, $class, $params);
}
function mysql_db_name($qlink, $row, $field='Database') {
mysqli_data_seek($qlink, $row);
$db = mysqli_fetch_assoc($qlink);
return $db[$field];
}
function mysql_fetch_field($qlink, $offset=null) {
if ($offset !== null)
mysqli_field_seek($qlink, $offset);
return mysqli_fetch_field($qlink);
}
function mysql_result($qlink, $offset, $field=0) {
if ($offset !== null)
mysqli_field_seek($qlink, $offset);
$row = mysqli_fetch_array($qlink);
return (!is_array($row) || !isset($row[$field]))
? false
: $row[$field];
}
function mysql_field_len($qlink, $offset) {
$field = mysqli_fetch_field_direct($qlink, $offset);
return is_object($field) ? $field->length : false;
}
function mysql_field_name($qlink, $offset) {
$field = mysqli_fetch_field_direct($qlink, $offset);
if (!is_object($field))
return false;
return empty($field->orgname) ? $field->name : $field->orgname;
}
function mysql_field_table($qlink, $offset) {
$field = mysqli_fetch_field_direct($qlink, $offset);
if (!is_object($field))
return false;
return empty($field->orgtable) ? $field->table : $field->orgtable;
}
function mysql_field_type($qlink, $offset) {
$field = mysqli_fetch_field_direct($qlink, $offset);
return is_object($field) ? $field->type : false;
}
function mysql_free_result($qlink) {
try {
mysqli_free_result($qlink);
} catch (Exception $e) {
return false;
}
return true;
}
les fonctions qui sont aussi similaires à ce mysql_connect()
, mysql_query()
type sont la version précédente PHP I. e (PHP 4) fonctionne et n'est plus utilisé .
ceux-ci sont remplacés par mysqli_connect()
, mysqli_query()
de même dans le dernier PHP5.
C'est la raison de l'erreur.
mysql_* fonctions ont été amortis (à partir de php 5.5 ) étant donné le fait que de meilleures fonctions et des structures de code ont été développés. Le fait que la fonction a été dépréciée signifie que plus aucun effort ne sera mis pour l'améliorer en termes de performance et de sécurité, ce qui signifie qu'il est moins future preuve .
si vous avez besoin de plus de raisons:
- mysql_* fonctions ne prennent pas en charge les requêtes préparées.
- mysql_* fonctions ne prennent pas en charge la liaison de paramètres.
- mysql_* les fonctions de l'absence de la fonctionnalité de la Programmation Orientée Objet.
- la liste continue ...