Was sind die technischen Gründe, warum man keine mysql_*
Funktionen verwenden sollte? (zB mysql_query()
, mysql_connect()
oder mysql_real_escape_string()
)?
Warum sollte ich etwas anderes verwenden, auch wenn es auf meiner Website funktioniert?
Wenn sie auf meiner Website nicht funktionieren, warum erhalte ich dann Fehler wie
Warnung: mysql_connect (): Keine solche Datei oder kein solches Verzeichnis
Antworten:
Die MySQL-Erweiterung:
Da es veraltet ist, ist Ihr Code durch seine Verwendung weniger zukunftssicher.
Die mangelnde Unterstützung für vorbereitete Anweisungen ist besonders wichtig, da sie eine klarere und weniger fehleranfällige Methode zum Escape- und Zitieren externer Daten bieten als das manuelle Escape-Verfahren mit einem separaten Funktionsaufruf.
Siehe den Vergleich von SQL-Erweiterungen .
quelle
PHP bietet drei verschiedene APIs für die Verbindung mit MySQL. Dies sind die
mysql
(ab PHP 7 entfernten)mysqli
undPDO
Erweiterungen.Die
mysql_*
Funktionen waren früher sehr beliebt, aber ihre Verwendung wird nicht mehr empfohlen. Das Dokumentationsteam erörtert die Datenbanksicherheitssituation, und die Schulung der Benutzer zur Abkehr von der häufig verwendeten ext / mysql-Erweiterung ist Teil davon ( siehe php.internals: ext / mysql veraltet ).Und je später PHP - Entwickler - Team hat die Entscheidung getroffen erzeugen
E_DEPRECATED
Fehler , wenn Benutzer zu MySQL verbinden, sei es durchmysql_connect()
,mysql_pconnect()
oder die implizite Verbindung Funktionalität eingebaut inext/mysql
.ext/mysql
wurde ab PHP 5.5 offiziell veraltet und ab PHP 7 entfernt .Sehen Sie die Red Box?
Wenn Sie eine
mysql_*
Funktionshandbuchseite aufrufen, wird ein rotes Kästchen angezeigt, in dem erklärt wird, dass es nicht mehr verwendet werden sollte.Warum
Bei der Abkehr von
ext/mysql
geht es nicht nur um Sicherheit, sondern auch darum, Zugriff auf alle Funktionen der MySQL-Datenbank zu haben.ext/mysql
wurde für MySQL 3.23 entwickelt und hat seitdem nur sehr wenige Ergänzungen erhalten, wobei die Kompatibilität mit dieser alten Version größtenteils erhalten blieb, wodurch die Wartung des Codes etwas schwieriger wird. Zu den fehlenden Funktionen, die nicht unterstützt werden,ext/mysql
gehören: ( aus dem PHP-Handbuch ).Grund, die
mysql_*
Funktion nicht zu verwenden :Oben zitierter Punkt aus Quentins Antwort
Die mangelnde Unterstützung für vorbereitete Anweisungen ist besonders wichtig, da sie eine klarere, weniger fehleranfällige Methode zum Escape- und Zitieren externer Daten bieten als das manuelle Escapezeichen mit einem separaten Funktionsaufruf.
Siehe den Vergleich von SQL-Erweiterungen .
Unterdrückungswarnungen unterdrücken
Während der Konvertierung von Code in
MySQLi
/PDO
könnenE_DEPRECATED
Fehler unterdrückt werden, indemerror_reporting
in php.ini festgelegt wird , dass sie ausgeschlossen werden sollenE_DEPRECATED:
Beachten Sie, dass dadurch auch andere Verfallswarnungen ausgeblendet werden , die jedoch möglicherweise für andere Zwecke als MySQL gelten. ( aus dem PHP-Handbuch )
Der Artikel PDO vs. MySQLi: Welches sollten Sie verwenden? von Dejan Marjanovic hilft Ihnen bei der Auswahl.
Und ein besserer Weg ist
PDO
, und ich schreibe jetzt ein einfachesPDO
Tutorial.Ein einfaches und kurzes PDO-Tutorial
Frage: Die erste Frage in meinem Kopf war: Was ist "gU"?
A. " PDO - PHP Data Objects - ist eine Datenbankzugriffsschicht, die eine einheitliche Methode für den Zugriff auf mehrere Datenbanken bietet."
Verbindung zu MySQL herstellen
Mit
mysql_*
Funktion oder wir können es auf die alte Art sagen (veraltet in PHP 5.5 und höher)Mit
PDO
: Sie müssen lediglich ein neuesPDO
Objekt erstellen . Der Konstruktor übernimmt Parameter zum Spezifizieren der DatenbankquellePDO
‚s Konstruktor meist vier Parameter, dieDSN
(Datenquellennamen) und gegebenenfallsusername
,password
.Hier denke ich, dass Sie mit allen außer vertraut sind
DSN
; das ist neu inPDO
. ADSN
ist im Grunde eine Reihe von Optionen, die angeben,PDO
welcher Treiber verwendet werden soll, und Verbindungsdetails. Weitere Informationen finden Sie unter PDO MySQL DSN .Hinweis: Sie können auch verwenden
charset=UTF-8
, aber manchmal verursacht es einen Fehler, daher ist es besser zu verwendenutf8
.Wenn ein Verbindungsfehler auftritt, wird ein
PDOException
Objekt ausgelöst, das abgefangen werden kann, um esException
weiter zu verarbeiten .Gut gelesen : Verbindungen und Verbindungsmanagement ¶
Sie können auch mehrere Treiberoptionen als Array an den vierten Parameter übergeben. Ich empfehle, den Parameter zu übergeben, der
PDO
in den Ausnahmemodus versetzt wird. Da einigePDO
Treiber native vorbereitete Anweisungen nicht unterstützen, wirdPDO
die Vorbereitung emuliert. Sie können diese Emulation auch manuell aktivieren. Um die nativen serverseitig vorbereiteten Anweisungen zu verwenden, sollten Sie sie explizit festlegenfalse
.Die andere
MySQL
Möglichkeit besteht darin, die Vorbereitungsemulation zu deaktivieren, die standardmäßig im Treiber aktiviert ist. Die Vorbereitungsemulation sollte jedoch deaktiviert sein, um einePDO
sichere Verwendung zu gewährleisten.Ich werde später erklären, warum die Vorbereitungsemulation deaktiviert werden sollte. Um einen Grund zu finden, überprüfen Sie bitte diesen Beitrag .
Es ist nur verwendbar, wenn Sie eine alte Version verwenden,
MySQL
die ich nicht empfehle.Unten finden Sie ein Beispiel dafür, wie Sie dies tun können:
Können wir Attribute nach der PDO-Erstellung festlegen?
Ja , wir können auch einige Attribute nach der PDO-Erstellung mit der
setAttribute
Methode festlegen :Fehlerbehandlung
Fehlerbehandlung ist in viel einfacher
PDO
alsmysql_*
.Eine gängige Praxis bei der Verwendung
mysql_*
ist:OR die()
ist kein guter Weg, um den Fehler zu behandeln, da wir die Sache nicht in behandeln könnendie
. Das Skript wird nur abrupt beendet und der Fehler wird dann auf dem Bildschirm angezeigt, den Sie Ihren Endbenutzern normalerweise NICHT anzeigen möchten, und blutige Hacker können Ihr Schema entdecken. Alternativ können die Rückgabewerte vonmysql_*
Funktionen häufig in Verbindung mit mysql_error () verwendet werden , um Fehler zu behandeln.PDO
bietet eine bessere Lösung: Ausnahmen. Alles , was wir mit tunPDO
sollte in einem gewickelt werdentry
-catch
Block. Wir könnenPDO
in einen von drei Fehlermodi wechseln, indem wir das Fehlermodusattribut festlegen. Im Folgenden sind drei Fehlerbehandlungsmodi aufgeführt.PDO::ERRMODE_SILENT
. Es setzt nur Fehlercodes und verhält sich fast so, alsmysql_*
müssten Sie jedes Ergebnis überprüfen und dann nachsehen$db->errorInfo();
, um die Fehlerdetails zu erhalten.PDO::ERRMODE_WARNING
ErhöhenE_WARNING
. (Laufzeitwarnungen (nicht schwerwiegende Fehler). Die Ausführung des Skripts wird nicht angehalten.)PDO::ERRMODE_EXCEPTION
: Ausnahmen auslösen. Es stellt einen Fehler dar, der von PDO ausgelöst wurde. Sie sollten keinPDOException
aus Ihrem eigenen Code werfen . Siehe Ausnahmen für weitere Informationen über Ausnahmen in PHP. Es verhält sich sehr ähnlichor die(mysql_error());
, wenn es nicht gefangen wird. Aber im Gegensatz zuor die()
, diePDOException
können anmutig abgefangen und behandelt werden , wenn Sie sich dazu entscheiden , dies zu tun.Gut gelesen :
Mögen:
Und Sie können es einwickeln
try
-catch
wie unten:Sie müssen nicht damit umgehen
try
- imcatch
Moment. Sie können es jederzeit angemessen fangen, aber ich empfehle Ihnen dringend,try
- zu verwendencatch
. Es kann auch sinnvoller sein, es außerhalb der Funktion abzufangen, die dasPDO
Zeug aufruft :Sie können auch damit umgehen
or die()
oder wir können sagen wiemysql_*
, aber es wird wirklich abwechslungsreich sein. Sie können die gefährlichen Fehlermeldungen in der Produktion ausblenden,display_errors off
indem Sie Ihr Fehlerprotokoll drehen und einfach lesen.Jetzt, nachdem vor allem die Dinge zu lesen, sind Sie wahrscheinlich denken: was zum Teufel das ist , wenn ich will nur starten einfach gelehnt
SELECT
,INSERT
,UPDATE
, oderDELETE
Aussagen? Keine Sorge, los geht's:Daten auswählen
Was Sie also tun,
mysql_*
ist:Jetzt in
PDO
können Sie dies wie folgt tun:Oder
Hinweis : Wenn Sie die Methode wie unten (
query()
) verwenden, gibt diese Methode einPDOStatement
Objekt zurück. Wenn Sie das Ergebnis abrufen möchten, verwenden Sie es wie oben beschrieben.In PDO-Daten wird es über die
->fetch()
Methode Ihres Anweisungshandles abgerufen . Bevor Sie fetch aufrufen, teilen Sie PDO am besten mit, wie die Daten abgerufen werden sollen. Im folgenden Abschnitt erkläre ich dies.Modi abrufen
Beachten Sie die Verwendung von
PDO::FETCH_ASSOC
imfetch()
undfetchAll()
Code oben. Dies weistPDO
an, die Zeilen als assoziatives Array mit den Feldnamen als Schlüssel zurückzugeben. Es gibt auch viele andere Abrufmodi, die ich nacheinander erläutern werde.Zunächst erkläre ich, wie der Abrufmodus ausgewählt wird:
Oben habe ich verwendet
fetch()
. Sie können auch verwenden:PDOStatement::fetchAll()
- Gibt ein Array zurück, das alle Ergebnismengenzeilen enthältPDOStatement::fetchColumn()
- Gibt eine einzelne Spalte aus der nächsten Zeile einer Ergebnismenge zurückPDOStatement::fetchObject()
- Ruft die nächste Zeile ab und gibt sie als Objekt zurück.PDOStatement::setFetchMode()
- Legen Sie den Standardabrufmodus für diese Anweisung festJetzt komme ich zum Abrufmodus:
PDO::FETCH_ASSOC
: Gibt ein Array zurück, das nach dem Spaltennamen indiziert ist und in Ihrer Ergebnismenge zurückgegeben wirdPDO::FETCH_BOTH
(Standard): Gibt ein Array zurück, das sowohl nach dem Spaltennamen als auch nach der 0-indizierten Spaltennummer indiziert ist, wie in Ihrer Ergebnismenge zurückgegebenEs gibt noch mehr Möglichkeiten! Lesen Sie mehr darüber in der
PDOStatement
Fetch-Dokumentation. .Abrufen der Zeilenanzahl :
Anstatt
mysql_num_rows
die Anzahl der zurückgegebenen Zeilen abzurufen, können Sie aPDOStatement
and dorowCount()
wie folgt abrufen:Abrufen der zuletzt eingefügten ID
Anweisungen einfügen und aktualisieren oder löschen
Was wir in der
mysql_*
Funktion tun , ist:Und in pdo kann dasselbe getan werden durch:
Führen Sie in der obigen Abfrage
PDO::exec
eine SQL-Anweisung aus und geben Sie die Anzahl der betroffenen Zeilen zurück.Einfügen und Löschen wird später behandelt.
Die obige Methode ist nur nützlich, wenn Sie keine Variable in der Abfrage verwenden. Wenn Sie jedoch eine Variable in einer Abfrage verwenden müssen, versuchen Sie niemals, wie oben beschrieben, eine vorbereitete Anweisung oder eine parametrisierte Anweisung zu verwenden .
Vorbereitete Aussagen
F. Was ist eine vorbereitete Erklärung und warum brauche ich sie?
A. Eine vorbereitete Anweisung ist eine vorkompilierte SQL-Anweisung, die mehrmals ausgeführt werden kann, indem nur die Daten an den Server gesendet werden.
Der typische Arbeitsablauf für die Verwendung einer vorbereiteten Anweisung lautet wie folgt ( zitiert aus Wikipedia drei 3 Punkte ):
Vorbereiten : Die Anweisungsvorlage wird von der Anwendung erstellt und an das Datenbankverwaltungssystem (DBMS) gesendet. Bestimmte Werte werden nicht angegeben und als Parameter, Platzhalter oder Bindungsvariablen bezeichnet (siehe
?
unten):INSERT INTO PRODUCT (name, price) VALUES (?, ?)
Das DBMS analysiert, kompiliert und führt eine Abfrageoptimierung für die Anweisungsvorlage durch und speichert das Ergebnis, ohne es auszuführen.
1.00
für den zweiten Parameter angegeben.Sie können eine vorbereitete Anweisung verwenden, indem Sie Platzhalter in Ihre SQL aufnehmen. Grundsätzlich gibt es drei ohne Platzhalter (versuchen Sie dies nicht mit einer Variablen über dem einen), einen mit unbenannten Platzhaltern und einen mit benannten Platzhaltern.
F : Nun, was Platzhalter genannt und wie kann ich sie nutzen?
A. Benannte Platzhalter. Verwenden Sie beschreibende Namen, denen ein Doppelpunkt vorangestellt ist, anstelle von Fragezeichen. Wir kümmern uns nicht um Position / Reihenfolge des Wertes im Namen Platzhalter:
bindParam(parameter,variable,data_type,length,driver_options)
Sie können auch mit einem Execute-Array binden:
Eine weitere nette Funktion für
OOP
Freunde ist, dass benannte Platzhalter Objekte direkt in Ihre Datenbank einfügen können, vorausgesetzt, die Eigenschaften stimmen mit den benannten Feldern überein. Zum Beispiel:F : Nun, was sind unbenannte Platzhalter und wie kann ich sie nutzen?
A. Lassen Sie uns ein Beispiel haben:
und
Oben sehen Sie diese
?
anstelle eines Namens wie in einem Namensplatzhalter. Im ersten Beispiel weisen wir den verschiedenen Platzhaltern Variablen zu ($stmt->bindValue(1, $name, PDO::PARAM_STR);
). Dann weisen wir diesen Platzhaltern Werte zu und führen die Anweisung aus. Im zweiten Beispiel geht das erste Array-Element zum ersten?
und das zweite zum zweiten?
.HINWEIS : Bei unbenannten Platzhaltern müssen wir auf die richtige Reihenfolge der Elemente im Array achten, die wir an die
PDOStatement::execute()
Methode übergeben.SELECT
,INSERT
,UPDATE
,DELETE
Vorbereitet AbfragenSELECT
::INSERT
::DELETE
::UPDATE
::HINWEIS:
Jedoch
PDO
und / oderMySQLi
sind nicht ganz sicher. Überprüfen Sie die Antwort. Sind PDO-vorbereitete Anweisungen ausreichend, um eine SQL-Injection zu verhindern? von ircmaxell . Außerdem zitiere ich einen Teil seiner Antwort:quelle
IN (...) construct
.function throwEx() { throw new Exception("You did selected not existng db"); } mysql_select_db("nonexistdb") or throwEx();
Es funktioniert, um Ausnahmen auszulösen.Doesn't support non-blocking, asynchronous queries
als Grund auf, mysql_ nicht zu verwenden - Sie sollten dies auch als Grund angeben, PDO nicht zu verwenden, da PDO dies auch nicht unterstützt. (aber MySQLi unterstützt es)Beginnen wir zunächst mit dem Standardkommentar, den wir allen geben:
Lassen Sie uns dies Satz für Satz durchgehen und erklären:
Sie werden nicht mehr gewartet und sind offiziell veraltet
Dies bedeutet, dass die PHP-Community die Unterstützung für diese sehr alten Funktionen allmählich einstellt. Sie werden wahrscheinlich in einer zukünftigen (aktuellen) Version von PHP nicht existieren! Die fortgesetzte Verwendung dieser Funktionen kann Ihren Code in (nicht so) ferner Zukunft beschädigen.
NEU! - ext / mysql ist ab PHP 5.5 offiziell veraltet!
Neuer! ext / mysql wurde in PHP 7 entfernt .
Stattdessen sollten Sie von vorbereiteten Aussagen erfahren
mysql_*
Die Erweiterung unterstützt keine vorbereiteten Anweisungen , was (unter anderem) eine sehr effektive Gegenmaßnahme gegen SQL Injection ist . Es wurde eine sehr schwerwiegende Sicherheitsanfälligkeit in MySQL-abhängigen Anwendungen behoben, die es Angreifern ermöglicht, auf Ihr Skript zuzugreifen und mögliche Abfragen in Ihrer Datenbank durchzuführen .Weitere Informationen finden Sie unter Wie kann ich die SQL-Injection in PHP verhindern?
Sehen Sie die Red Box?
Wenn Sie zu einer
mysql
Funktionshandbuchseite gehen , sehen Sie ein rotes Kästchen, in dem erklärt wird, dass es nicht mehr verwendet werden sollte.Verwenden Sie entweder PDO oder MySQLi
Es gibt bessere, robustere und gut ausgebaute Alternativen, PDO - PHP Database Object , das einen vollständigen OOP-Ansatz für die Datenbankinteraktion bietet, und MySQLi , eine MySQL-spezifische Verbesserung.
quelle
IN (...) construct
.Benutzerfreundlichkeit
Die analytischen und synthetischen Gründe wurden bereits erwähnt. Für Neulinge gibt es einen größeren Anreiz, die datierten mysql_-Funktionen nicht mehr zu verwenden.
Zeitgemäße Datenbank-APIs sind einfach einfacher zu verwenden.
Es sind hauptsächlich die gebundenen Parameter, die den Code vereinfachen können. Und mit hervorragenden Tutorials (wie oben gezeigt) ist der Übergang zu PDO nicht allzu mühsam.
Das sofortige Umschreiben einer größeren Codebasis erfordert jedoch Zeit. Existenzberechtigung für diese Zwischenalternative:
Äquivalente pdo_ * -Funktionen anstelle von
mysql_ *Mit < pdo_mysql.php > können Sie mit minimalem Aufwand von den alten mysql_-Funktionen wechseln . Es werden
pdo_
Funktions-Wrapper hinzugefügt, die ihremysql_
Gegenstücke ersetzen .Einfach in jedem Aufrufskript, das mit der Datenbank interagieren muss.
include_once(
"pdo_mysql.php"
);
Entfernen Sie das
Funktionspräfix überall und ersetzen Sie es durchmysql_
pdo_
.mysql_
connect()
wirdpdo_
connect()
mysql_
query()
wirdpdo_
query()
mysql_
num_rows()
wirdpdo_
num_rows()
mysql_
insert_id()
wirdpdo_
insert_id()
mysql_
fetch_array()
wirdpdo_
fetch_array()
mysql_
fetch_assoc()
wirdpdo_
fetch_assoc()
mysql_
real_escape_string()
wirdpdo_
real_escape_string()
Ihr Code funktioniert gleich und sieht meistens immer noch gleich aus:
Et voilà.
Ihr Code verwendet PDO.
Jetzt ist es Zeit, es tatsächlich zu nutzen .
Gebundene Parameter können einfach zu verwenden sein
Sie benötigen lediglich eine weniger unhandliche API.
pdo_query()
Fügt eine sehr einfache Unterstützung für gebundene Parameter hinzu. Das Konvertieren von altem Code ist unkompliziert:Verschieben Sie Ihre Variablen aus der SQL-Zeichenfolge.
pdo_query()
.?
als Platzhalter, an denen sich die Variablen zuvor befanden.'
einfache Anführungszeichen, die zuvor Zeichenfolgenwerte / -variablen enthalten haben.Der Vorteil wird bei längerem Code offensichtlicher.
Oft werden String-Variablen nicht nur in SQL interpoliert, sondern mit dazwischen liegenden Escape-Aufrufen verknüpft.
Mit
?
Platzhaltern müssen Sie sich nicht darum kümmern:Denken Sie daran, dass pdo_ * weiterhin entweder oder zulässt .
Entkomme einer Variablen einfach nicht und binde sie in dieselbe Abfrage.
:named
sind später auch Platzhalterlisten erlaubt .Noch wichtiger ist, dass Sie $ _REQUEST [] -Variablen sicher hinter jeder Abfrage übergeben können. Wenn übermittelte
<form>
Felder genau mit der Datenbankstruktur übereinstimmen, ist sie noch kürzer:So viel Einfachheit. Aber lassen Sie uns noch einmal auf einige Ratschläge zum Umschreiben und technische Gründe zurückkommen, warum Sie vielleicht loswerden
und entkommen möchten .mysql_
Korrigieren oder entfernen Sie alle Oldschool-
sanitize()
FunktionenWenn Sie alle
Aufrufemysql_
pdo_query
mit gebundenen Parametern in konvertiert haben , entfernen Sie alle redundantenpdo_real_escape_string
Aufrufe.Insbesondere sollten Sie jede reparieren
sanitize
oderclean
oderfilterThis
oderclean_data
Funktionen wie durch datiert Tutorials in dem einem oder dem anderen beworben:Der auffälligste Fehler hier ist der Mangel an Dokumentation. Noch wichtiger ist, dass die Reihenfolge der Filterung genau in der falschen Reihenfolge war.
Die richtige Reihenfolge gewesen wäre: deprecatedly
stripslashes
als innerste Anruf, danntrim
, danachstrip_tags
,htmlentities
für Ausgabekontext, und nur schließlich die_escape_string
seine Anwendung direkt auf die SQL intersparsing vorausgehen sollte.Aber als ersten Schritt einfach den
_real_escape_string
Anruf loswerden .Möglicherweise müssen Sie den Rest Ihrer
sanitize()
Funktion vorerst beibehalten, wenn Ihr Datenbank- und Anwendungsfluss HTML-kontextsichere Zeichenfolgen erwartet. Fügen Sie einen Kommentar hinzu, der nur HTML anwendet, das fortan maskiert wird.Die Behandlung von Zeichenfolgen / Werten wird an PDO und seine parametrisierten Anweisungen delegiert.
Wenn
stripslashes()
in Ihrer Desinfektionsfunktion etwas erwähnt wurde, kann dies auf eine übergeordnete Aufsicht hinweisen.Das war normalerweise da, um Schäden (doppeltes Entkommen) von den Veralteten rückgängig zu machen
magic_quotes
. Was jedoch am besten zentral festgelegt wird , nicht String für String.Verwenden Sie einen der Userland-Umkehrungsansätze . Dann entfernen Sie die
stripslashes()
in dersanitize
Funktion.Wie sich vorbereitete Aussagen unterscheiden
Wenn Sie Zeichenfolgenvariablen in die SQL-Abfragen verschlüsseln, wird dies nicht nur für Sie komplizierter. Es ist für MySQL auch eine enorme Anstrengung, Code und Daten wieder zu trennen.
SQL-Injections sind einfach dann, wenn Daten in den Codekontext übergehen. Ein Datenbankserver kann später nicht erkennen, wo PHP ursprünglich Variablen zwischen Abfrageklauseln geklebt hat.
Mit gebundenen Parametern trennen Sie SQL-Code und SQL-Kontextwerte in Ihrem PHP-Code. Aber es wird hinter den Kulissen nicht wieder gemischt (außer bei PDO :: EMULATE_PREPARES). Ihre Datenbank empfängt die unveränderten SQL-Befehle und 1: 1-Variablenwerte.
Während diese Antwort betont, dass Sie sich um die Lesbarkeitsvorteile des Fallens kümmern sollten
. Gelegentlich gibt es aufgrund dieser sichtbaren und technischen Daten- / Codetrennung auch einen Leistungsvorteil (wiederholte INSERTs mit nur unterschiedlichen Werten).mysql_
Beachten Sie, dass die Parameterbindung immer noch keine magische Lösung aus einer Hand für alle SQL-Injektionen ist. Es behandelt die häufigste Verwendung für Daten / Werte. Es können jedoch keine Spaltennamen- / Tabellenkennungen in die Whitelist aufgenommen, keine Hilfe bei der Erstellung dynamischer Klauseln oder nur einfache Array-Wertelisten verwendet werden.
Verwendung von Hybrid-PDOs
Diese
pdo_*
Wrapper-Funktionen bilden eine codierungsfreundliche Stop-Gap-API. (Es ist so ziemlich das, wasMYSQLI
hätte sein können, wenn es nicht die eigenwillige Funktionssignaturverschiebung gegeben hätte). Sie legen auch meistens die echte gU offen.Das Umschreiben muss nicht bei der Verwendung der neuen Funktionsnamen pdo_ aufhören. Sie können jede pdo_query () einzeln in einen einfachen Aufruf von $ pdo-> prepare () -> execute () umwandeln.
Es ist jedoch am besten, wieder mit der Vereinfachung zu beginnen. Zum Beispiel das gemeinsame Abrufen von Ergebnissen:
Kann durch nur eine foreach-Iteration ersetzt werden:
Oder noch besser ein direkter und vollständiger Array-Abruf:
In den meisten Fällen erhalten Sie hilfreichere Warnungen als PDO oder mysql_ normalerweise nach fehlgeschlagenen Abfragen.
Andere Optionen
Dies visualisierte hoffentlich einige praktische Gründe und einen lohnenden Weg, um fallen zu lassen
.mysql_
Einfach umschalten auf pdoschneidet es nicht ganz.
pdo_query()
ist auch nur ein Frontend drauf.Sofern Sie nicht auch die Parameterbindung einführen oder etwas anderes aus der schöneren API verwenden können, ist dies ein sinnloser Wechsel. Ich hoffe, es ist einfach genug dargestellt, um die Entmutigung von Neuankömmlingen nicht weiter zu fördern. (Bildung funktioniert normalerweise besser als Verbot.)
Während es sich für die einfachste Kategorie qualifiziert, die möglicherweise funktionieren könnte, ist es auch immer noch sehr experimenteller Code. Ich habe es gerade über das Wochenende geschrieben. Es gibt jedoch eine Vielzahl von Alternativen. Google einfach nach PHP-Datenbankabstraktion und stöbere ein wenig. Es gab und wird immer viele hervorragende Bibliotheken für solche Aufgaben geben.
Wenn Sie Ihre Datenbankinteraktion weiter vereinfachen möchten, sind Mapper wie Paris / Idiorm einen Versuch wert. So wie niemand mehr das langweilige DOM in JavaScript verwendet, müssen Sie heutzutage keine rohe Datenbankschnittstelle mehr babysitten.
quelle
pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);
Funktion - dh:pdo_query("INSERT INTO users VALUES (?, ?, ?), $_POST); $_POST = array( 'username' => 'lawl', 'password' => '123', 'is_admin' => 'true');
pdo_real_escape_string()
<- Ist das überhaupt eine echte Funktion, ich kann keine Dokumentation dafür finden? Bitte posten Sie eine Quelle dafür.Die
mysql_
Funktionen:quelle
mysqli_
mysql_*
Funktion ist eine Shell in MySQL-Funktionen für neuere PHP-Versionen. Also, auch wenn die alte Client-Bibliothek nicht mehr gepflegt wird, wird mysqlnd gepflegt :)Apropos technische Gründe, es gibt nur wenige, äußerst spezifische und selten verwendete. Höchstwahrscheinlich werden Sie sie niemals in Ihrem Leben verwenden.
Vielleicht bin ich zu unwissend, aber ich hatte nie die Gelegenheit, sie zu benutzen
Wenn Sie sie brauchen - dies sind zweifellos technische Gründe, um von der MySQL-Erweiterung zu etwas Stilvollerem und Modernem überzugehen.
Es gibt jedoch auch einige nichttechnische Probleme, die Ihre Erfahrung etwas erschweren können
Dieses letztere Problem ist ein Problem.
Aber meiner Meinung nach ist die vorgeschlagene Lösung auch nicht besser.
Es scheint mir ein zu idealistischer Traum zu sein, dass all diese PHP-Benutzer lernen werden, wie man SQL-Abfragen gleichzeitig richtig behandelt. Höchstwahrscheinlich würden sie mysql_ * nur mechanisch in mysqli_ * ändern, wobei der Ansatz unverändert bleibt . Vor allem, weil mysqli die Verwendung vorbereiteter Aussagen unglaublich schmerzhaft und mühsam macht.
Ganz zu schweigen davon, dass native vorbereitete Anweisungen nicht ausreichen, um vor SQL-Injektionen zu schützen , und weder mysqli noch PDO bieten eine Lösung.
Anstatt diese ehrliche Erweiterung zu bekämpfen, würde ich es vorziehen, falsche Praktiken zu bekämpfen und die Menschen auf die richtige Weise zu erziehen.
Es gibt auch einige falsche oder nicht signifikante Gründe, wie z
mysql_query("CALL my_proc");
seit Ewigkeiten verwendet)Der letzte ist ein interessanter Punkt. Obwohl mysql ext keine nativen vorbereiteten Anweisungen unterstützt, sind sie aus Sicherheitsgründen nicht erforderlich. Wir können vorbereitete Anweisungen leicht mit manuell behandelten Platzhaltern fälschen (genau wie bei PDO):
voila , alles ist parametrisiert und sicher.
Aber okay, wenn Ihnen das rote Kästchen im Handbuch nicht gefällt, tritt ein Problem der Wahl auf: mysqli oder PDO?
Nun, die Antwort wäre wie folgt:
Wenn Sie, wie die große Mehrheit der PHP-Leute, unformatierte API-Aufrufe direkt im Anwendungscode verwenden (was im Wesentlichen eine falsche Praxis ist), ist PDO die einzige Wahl , da diese Erweiterung vorgibt, nicht nur API, sondern eine Semi-DAL zu sein. immer noch unvollständig, bietet aber viele wichtige Funktionen, von denen zwei PDO kritisch von mysqli unterscheiden:
Wenn Sie also ein durchschnittlicher PHP-Benutzer sind und sich eine Menge Kopfschmerzen ersparen möchten, wenn Sie native vorbereitete Anweisungen verwenden, ist PDO - wieder - die einzige Wahl.
PDO ist jedoch auch keine Silberkugel und hat seine Schwierigkeiten.
Also schrieb ich Lösungen für alle gängigen Fallstricke und komplexen Fälle im PDO-Tag-Wiki
Trotzdem fehlen allen, die über Erweiterungen sprechen, immer die 2 wichtigen Fakten über Mysqli und PDO:
Vorbereitete Aussage ist keine Silberkugel . Es gibt dynamische Bezeichner, die mit vorbereiteten Anweisungen nicht gebunden werden können. Es gibt dynamische Abfragen mit einer unbekannten Anzahl von Parametern, was das Erstellen von Abfragen zu einer schwierigen Aufgabe macht.
Weder mysqli_ * noch PDO-Funktionen sollten im Anwendungscode enthalten sein.
Es sollte eine Abstraktionsschicht zwischen ihnen und dem Anwendungscode geben, die den ganzen schmutzigen Job des Bindens, Schleifens, der Fehlerbehandlung usw. im Inneren erledigt und den Anwendungscode trocken und sauber macht. Speziell für komplexe Fälle wie das dynamische Erstellen von Abfragen.
Es reicht also nicht aus, nur auf PDO oder mysqli umzusteigen. Man muss einen ORM oder einen Abfrage-Generator oder eine beliebige Datenbankabstraktionsklasse verwenden, anstatt unformatierte API-Funktionen in ihrem Code aufzurufen.
Und im Gegenteil - wenn Sie eine Abstraktionsschicht zwischen Ihrem Anwendungscode und der MySQL-API haben - spielt es keine Rolle, welche Engine verwendet wird. Sie können mysql ext verwenden, bis es veraltet ist, und dann Ihre Abstraktionsklasse einfach in eine andere Engine umschreiben, wobei der gesamte Anwendungscode intakt ist.
Hier sind einige Beispiele, die auf meiner safemysql-Klasse basieren, um zu zeigen, wie eine solche Abstraktionsklasse sein sollte:
Vergleichen Sie diese eine einzelne Zeile mit der Menge an Code, die Sie mit PDO benötigen .
Vergleichen Sie dann mit der verrückten Menge an Code, die Sie benötigen, mit rohen, von Mysqli vorbereiteten Anweisungen. Beachten Sie, dass die Fehlerbehandlung, Profilerstellung und Abfrageprotokollierung bereits integriert sind und ausgeführt werden.
Vergleichen Sie es mit üblichen PDO-Einfügungen, wenn jeder einzelne Feldname sechs- bis zehnmal wiederholt wird - in all diesen zahlreichen benannten Platzhaltern, Bindungen und Abfragedefinitionen.
Ein anderes Beispiel:
Sie können kaum ein Beispiel für PDO finden, um einen solchen praktischen Fall zu behandeln.
Und es wird zu wortreich und höchstwahrscheinlich unsicher sein.
Noch einmal - es ist nicht nur ein roher Treiber, der Ihr Anliegen sein sollte, sondern eine Abstraktionsklasse, die nicht nur für alberne Beispiele aus dem Anfängerhandbuch nützlich ist, sondern auch zur Lösung aller realen Probleme.
quelle
mysql_*
macht es sehr einfach, Schwachstellen zu finden. Da PHP von vielen Anfängern verwendet wird,mysql_*
ist es in der Praxis aktiv schädlich, auch wenn es theoretisch problemlos verwendet werden kann.everything is parameterized and safe
- Es kann parametrisiert werden, aber Ihre Funktion verwendet keine wirklich vorbereiteten Anweisungen.Not under active development
nur für diese Zusammensetzung "0,01%"? Wenn Sie etwas mit dieser Stand-Still-Funktion erstellen, Ihre MySQL-Version in einem Jahr aktualisieren und mit einem nicht funktionierenden System enden, sind sicher plötzlich sehr viele Leute in diesen '0,01%'. Ich würde das sagendeprecated
und binnot under active development
eng verwandt. Sie können sagen, dass es "keinen [würdigen] Grund" dafür gibt, aber Tatsache ist, dass die Wahl zwischen den Optionenno active development
fast genauso schlecht ist, wiedeprecated
ich sagen würde?Es gibt viele Gründe, aber der vielleicht wichtigste ist, dass diese Funktionen unsichere Programmierpraktiken fördern, da sie vorbereitete Anweisungen nicht unterstützen. Vorbereitete Anweisungen verhindern SQL-Injection-Angriffe.
Wenn Sie
mysql_*
Funktionen verwenden, müssen Sie daran denken, vom Benutzer bereitgestellte Parameter auszuführenmysql_real_escape_string()
. Wenn Sie nur an einer Stelle vergessen oder nur einen Teil der Eingabe verlassen, ist Ihre Datenbank möglicherweise einem Angriff ausgesetzt.Die Verwendung vorbereiteter Anweisungen in
PDO
odermysqli
wird es so machen, dass diese Art von Programmierfehlern schwieriger zu machen sind.quelle
Weil es (unter anderem) viel schwieriger ist, sicherzustellen, dass die Eingabedaten bereinigt sind. Wenn Sie parametrisierte Abfragen verwenden, wie dies bei PDO oder mysqli der Fall ist, können Sie das Risiko vollständig vermeiden.
Als Beispiel könnte jemand
"enhzflep); drop table users"
einen Benutzernamen verwenden. Mit den alten Funktionen können mehrere Anweisungen pro Abfrage ausgeführt werden, sodass so etwas wie dieser böse Mistkerl eine ganze Tabelle löschen kann.Wenn man PDO von mysqli verwenden würde, wäre der Benutzername am Ende
"enhzflep); drop table users"
.Siehe bobby-tables.com .
quelle
The old functions will allow executing of multiple statements per query
- Nein, das werden sie nicht. Diese Art der Injektion ist mit ext / mysql nicht möglich - diese Art der Injektion ist mit PHP und MySQL nur möglich, wenn MySQLi und diemysqli_multi_query()
Funktion verwendet werden. Die Art der Injektion, die mit ext / mysql und nicht entkappten Zeichenfolgen möglich ist' OR '1' = '1
, besteht darin, Daten aus der Datenbank zu extrahieren, auf die nicht zugegriffen werden sollte. In bestimmten Situationen ist es möglich, Unterabfragen einzufügen, es ist jedoch immer noch nicht möglich, die Datenbank auf diese Weise zu ändern.Diese Antwort soll zeigen, wie trivial es ist, schlecht geschriebenen PHP-Benutzervalidierungscode zu umgehen, wie (und was) diese Angriffe funktionieren und wie die alten MySQL-Funktionen durch eine sicher vorbereitete Anweisung ersetzt werden - und im Grunde, warum StackOverflow-Benutzer (wahrscheinlich mit viel Wiederholung) bellen neue Benutzer an, die Fragen stellen, um ihren Code zu verbessern.
Zunächst können Sie diese Test-MySQL-Datenbank erstellen (ich habe meine Vorbereitung aufgerufen):
Nachdem dies erledigt ist, können wir zu unserem PHP-Code wechseln.
Nehmen wir an, das folgende Skript ist der Überprüfungsprozess für einen Administrator auf einer Website (vereinfacht, funktioniert aber, wenn Sie es kopieren und zum Testen verwenden):
Scheint auf den ersten Blick legitim genug.
Der Benutzer muss ein Login und ein Passwort eingeben, oder?
Genial, geben Sie nicht Folgendes ein:
und einreichen.
Die Ausgabe ist wie folgt:
Super! Versuchen wir nun, den tatsächlichen Benutzernamen und das Passwort wie erwartet zu verwenden:
Tolle! Hi-Fives rundum, der Code hat einen Administrator korrekt verifiziert. Es ist perfekt!
Nicht wirklich. Nehmen wir an, der Benutzer ist eine kluge kleine Person. Nehmen wir an, die Person bin ich.
Geben Sie Folgendes ein:
Und die Ausgabe ist:
Herzlichen Glückwunsch, Sie haben mir nur erlaubt, Ihren Abschnitt nur für Super-geschützte Administratoren einzugeben, wobei ich einen falschen Benutzernamen und ein falsches Passwort eingegeben habe. Im Ernst, wenn Sie mir nicht glauben, erstellen Sie die Datenbank mit dem von mir bereitgestellten Code und führen Sie diesen PHP-Code aus - der auf den ersten Blick WIRKLICH den Benutzernamen und das Passwort ziemlich gut zu überprüfen scheint.
Als Antwort ist das der Grund, warum Sie angeschrien werden.
Schauen wir uns also an, was schief gelaufen ist und warum ich gerade in Ihre Super-Admin-Only-Bat-Höhle gekommen bin. Ich nahm eine Vermutung an und nahm an, dass Sie mit Ihren Eingaben nicht vorsichtig waren und gab sie einfach direkt an die Datenbank weiter. Ich habe die Eingabe so konstruiert, dass die Abfrage, die Sie tatsächlich ausgeführt haben, geändert wird. Also, was sollte es sein und was war es am Ende?
Das ist die Abfrage, aber wenn wir die Variablen durch die tatsächlich verwendeten Eingaben ersetzen, erhalten wir Folgendes:
Sehen Sie, wie ich mein "Passwort" so konstruiert habe, dass es zuerst das einfache Anführungszeichen um das Passwort schließt und dann einen völlig neuen Vergleich einführt? Aus Sicherheitsgründen habe ich dann eine weitere "Zeichenfolge" hinzugefügt, damit das einfache Anführungszeichen wie erwartet in dem Code geschlossen wird, den wir ursprünglich hatten.
Hier geht es jedoch nicht darum, dass Leute Sie jetzt anschreien, sondern darum, Ihnen zu zeigen, wie Sie Ihren Code sicherer machen können.
Okay, was ist schief gelaufen und wie können wir das beheben?
Dies ist ein klassischer SQL-Injection-Angriff. Eine der einfachsten für diese Angelegenheit. Auf der Skala der Angriffsvektoren ist dies ein Kleinkind, das einen Panzer angreift - und gewinnt.
Wie schützen wir Ihren heiligen Verwaltungsbereich und machen ihn schön und sicher? Das erste, was Sie tun müssen, ist, die Verwendung dieser wirklich alten und veralteten
mysql_*
Funktionen einzustellen . Ich weiß, Sie sind einem Tutorial gefolgt, das Sie online gefunden haben, und es funktioniert, aber es ist alt, veraltet und innerhalb weniger Minuten habe ich es einfach hinter mich gebracht, ohne auch nur ins Schwitzen zu geraten.Jetzt haben Sie die besseren Möglichkeiten, mysqli_ oder PDO zu verwenden . Ich persönlich bin ein großer Fan von PDO, daher werde ich PDO im Rest dieser Antwort verwenden. Es gibt Vor- und Nachteile, aber ich persönlich finde, dass die Vor- und Nachteile die Nachteile bei weitem überwiegen. Es ist portabel über mehrere Datenbank-Engines hinweg - egal ob Sie MySQL oder Oracle verwenden oder fast alles - nur durch Ändern der Verbindungszeichenfolge verfügt es über alle ausgefallenen Funktionen, die wir verwenden möchten, und es ist schön und sauber. Ich mag sauber.
Schauen wir uns diesen Code noch einmal an, diesmal mit einem PDO-Objekt geschrieben:
Die Hauptunterschiede sind, dass es keine
mysql_*
Funktionen mehr gibt . Dies geschieht alles über ein PDO-Objekt, zweitens wird eine vorbereitete Anweisung verwendet. Was ist eine vorgefertigte Aussage, die Sie fragen? Auf diese Weise können Sie der Datenbank vor dem Ausführen einer Abfrage mitteilen, welche Abfrage ausgeführt werden soll. In diesem Fall teilen wir der Datenbank mit: "Hallo, ich werde eine select-Anweisung ausführen, die id, userid und pass von den Tabellenbenutzern wünscht, wobei die userid eine Variable und der pass auch eine Variable ist."Dann übergeben wir in der execute-Anweisung der Datenbank ein Array mit allen Variablen, die sie jetzt erwartet.
Die Ergebnisse sind fantastisch. Versuchen wir noch einmal die Kombinationen aus Benutzername und Passwort:
Benutzer wurde nicht verifiziert. Genial.
Wie wäre es mit:
Oh, ich war nur ein bisschen aufgeregt, es hat funktioniert: Der Scheck hat bestanden. Wir haben einen verifizierten Administrator!
Versuchen wir nun die Daten, die ein kluger Kerl eingeben würde, um an unserem kleinen Verifizierungssystem vorbeizukommen:
Dieses Mal erhalten wir Folgendes:
Dies ist der Grund, warum Sie beim Posten von Fragen angeschrien werden - weil die Leute sehen können, dass Ihr Code umgangen werden kann, ohne es zu versuchen. Verwenden Sie diese Frage und Antwort, um Ihren Code zu verbessern, ihn sicherer zu machen und aktuelle Funktionen zu verwenden.
Schließlich soll dies nicht heißen, dass dies PERFEKTER Code ist. Es gibt noch viel mehr Dinge, die Sie tun können, um es zu verbessern. Verwenden Sie beispielsweise Hash-Passwörter. Stellen Sie beispielsweise sicher, dass Sie beim Speichern sensibler Informationen in der Datenbank diese nicht im Klartext speichern und über mehrere Überprüfungsebenen verfügen - aber wirklich, wenn Wenn Sie nur Ihren alten Code ändern, der zu Injektionen neigt, werden Sie auf dem Weg zum Schreiben von gutem Code GUT sein - und die Tatsache, dass Sie so weit gekommen sind und noch lesen, gibt mir ein Gefühl der Hoffnung, dass Sie diesen Typ nicht nur implementieren werden von Code beim Schreiben Ihrer Websites und Anwendungen, aber dass Sie die anderen Dinge, die ich gerade erwähnt habe, recherchieren könnten - und mehr. Schreiben Sie den bestmöglichen Code, nicht den grundlegendsten Code, der kaum funktioniert.
quelle
mysql_*
an sich nicht unsicher ist, aber unsicheren Code durch schlechte Tutorials und das Fehlen einer geeigneten API zur Vorbereitung von Anweisungen fördert.Die MySQL-Erweiterung ist die älteste der drei und war die ursprüngliche Art und Weise, wie Entwickler mit MySQL kommunizierten. Diese Erweiterung wird jetzt zugunsten der beiden anderen Alternativen aufgrund von Verbesserungen in neueren Versionen von PHP und MySQL abgelehnt .
MySQLi ist die 'verbesserte' Erweiterung für die Arbeit mit MySQL-Datenbanken. Es nutzt die Funktionen, die in neueren Versionen des MySQL-Servers verfügbar sind, stellt dem Entwickler sowohl eine funktionsorientierte als auch eine objektorientierte Schnittstelle zur Verfügung und erledigt einige andere nützliche Dinge.
PDO bietet eine API, die die meisten Funktionen konsolidiert, die zuvor auf die wichtigsten Datenbankzugriffserweiterungen verteilt waren, z. B. MySQL, PostgreSQL, SQLite, MSSQL usw. Die Schnittstelle stellt dem Programmierer Objekte auf hoher Ebene zur Verfügung, um mit Datenbankverbindungen, Abfragen und zu arbeiten Ergebnismengen und Treiber auf niedriger Ebene führen die Kommunikation und das Ressourcenhandling mit dem Datenbankserver durch. In PDO wird viel diskutiert und gearbeitet, und es wird als geeignete Methode für die Arbeit mit Datenbanken in modernem, professionellem Code angesehen.
quelle
Ich finde die obigen Antworten sehr lang, um es zusammenzufassen:
Quelle: MySQLi-Übersicht
Wie in den obigen Antworten erläutert, sind die Alternativen zu mysql mysqli und PDO (PHP Data Objects).
Sowohl MySQLi als auch PDO wurden in PHP 5.0 eingeführt, während MySQL vor PHP 3.0 eingeführt wurde. Zu beachten ist, dass MySQL in PHP5.x enthalten ist, in späteren Versionen jedoch veraltet ist.
quelle
Es ist möglich, fast alle
mysql_*
Funktionen mit mysqli oder PDO zu definieren . Fügen Sie sie einfach über Ihre alte PHP-Anwendung ein, und es funktioniert unter PHP7. Meine Lösung hier .quelle
Die Funktionen, die diesem Typ ähnlich sind
mysql_connect()
,mysql_query()
sind die PHP-Funktionen der Vorgängerversion (dh PHP 4) und werden jetzt nicht verwendet.Diese werden ersetzt durch
mysqli_connect()
,mysqli_query()
ähnlich in der neuesten PHP5.Dies ist der Grund für den Fehler.
quelle
MySQL ist in PHP 5.5.0 veraltet und in PHP 7.0.0 entfernt worden. Für eine große und alte Anwendung ist es schwierig, jede Funktion zu suchen und zu ersetzen.
Wir können MySQL-Funktionen verwenden, indem wir eine Wrapper-Funktion für jeden der folgenden Code erstellen. Klick hier
quelle
mysql_ * -Funktionen waren (ab PHP 5.5 ) veraltet , da bessere Funktionen und Codestrukturen entwickelt wurden. Die Tatsache, dass die Funktion veraltet war, bedeutet, dass keine weiteren Anstrengungen unternommen werden, um sie in Bezug auf Leistung und Sicherheit zu verbessern, was bedeutet, dass sie weniger zukunftssicher ist .
Wenn Sie weitere Gründe benötigen:
quelle