Wie kann ich die SQL-Injection in PHP verhindern?

Sep 13 2008

Wenn Benutzereingaben ohne Änderung in eine SQL-Abfrage eingefügt werden, ist die Anwendung wie im folgenden Beispiel für SQL-Injection anfällig :

$unsafe_variable = $_POST['user_input']; 

mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");

Das liegt daran, dass der Benutzer so etwas eingeben value'); DROP TABLE table;--kann und die Abfrage wie folgt lautet:

INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')

Was kann getan werden, um dies zu verhindern?

Antworten

9121 PeeHaa Sep 13 2008 at 19:30

Verwenden Sie vorbereitete Anweisungen und parametrisierte Abfragen. Hierbei handelt es sich um SQL-Anweisungen, die getrennt von Parametern an den Datenbankserver gesendet und von diesem analysiert werden. Auf diese Weise kann ein Angreifer kein schädliches SQL einschleusen.

Sie haben grundsätzlich zwei Möglichkeiten, um dies zu erreichen:

  1. Verwenden von PDO (für jeden unterstützten Datenbanktreiber):

    $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
    
    $stmt->execute([ 'name' => $name ]);
    
    foreach ($stmt as $row) {
        // Do something with $row
    }
    
  2. Verwenden von MySQLi (für MySQL):

    $stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?'); $stmt->bind_param('s', $name); // 's' specifies the variable type => 'string' $stmt->execute();
    
    $result = $stmt->get_result();
    while ($row = $result->fetch_assoc()) {
        // Do something with $row
    }
    

Wenn Sie in eine andere Datenbank als MySQL sich verbinden, gibt es einen Treiber spezifische zweite Option , die Sie sich beziehen können (zum Beispiel pg_prepare()und pg_execute()für PostgreSQL). PDO ist die universelle Option.


Richtiges Einrichten der Verbindung

Beachten Sie, dass beim PDOZugriff auf eine MySQL-Datenbank standardmäßig nicht wirklich vorbereitete Anweisungen verwendet werden . Um dies zu beheben, müssen Sie die Emulation vorbereiteter Anweisungen deaktivieren. Ein Beispiel für das Erstellen einer Verbindung mit PDO ist:

$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'password');

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

Im obigen Beispiel ist der Fehlermodus nicht unbedingt erforderlich, es wird jedoch empfohlen, ihn hinzuzufügen . Auf diese Weise hört das Skript nicht mit einem auf, Fatal Errorwenn etwas schief geht. Und es gibt dem Entwickler die Möglichkeit, catchFehler zu machen, die thrown wie PDOExceptions sind.

Was jedoch obligatorisch ist, ist die erste setAttribute()Zeile, in der PDO angewiesen wird, emulierte vorbereitete Anweisungen zu deaktivieren und echte vorbereitete Anweisungen zu verwenden. Dadurch wird sichergestellt, dass die Anweisung und die Werte nicht von PHP analysiert werden, bevor sie an den MySQL-Server gesendet werden (sodass ein möglicher Angreifer keine Chance hat, schädliches SQL einzuschleusen).

Obwohl Sie die charsetin den Optionen des Konstruktors festlegen können , ist es wichtig zu beachten, dass "ältere" Versionen von PHP (vor 5.3.6) den Zeichensatzparameter im DSN stillschweigend ignorierten .


Erläuterung

Die SQL-Anweisung, an die Sie übergeben, preparewird vom Datenbankserver analysiert und kompiliert. Durch Angabe von Parametern (entweder ein ?oder ein benannter Parameter wie :nameim obigen Beispiel) teilen Sie dem Datenbankmodul mit, nach was Sie filtern möchten. Wenn Sie dann aufrufen execute, wird die vorbereitete Anweisung mit den von Ihnen angegebenen Parameterwerten kombiniert.

Wichtig hierbei ist, dass die Parameterwerte mit der kompilierten Anweisung kombiniert werden, nicht mit einer SQL-Zeichenfolge. Bei der SQL-Injection wird das Skript dazu verleitet, beim Erstellen von SQL zum Senden an die Datenbank schädliche Zeichenfolgen einzuschließen. Indem Sie das eigentliche SQL getrennt von den Parametern senden, begrenzen Sie das Risiko, dass Sie etwas erhalten, das Sie nicht beabsichtigt haben.

Alle Parameter, die Sie bei Verwendung einer vorbereiteten Anweisung senden, werden nur als Zeichenfolgen behandelt (obwohl das Datenbankmodul möglicherweise einige Optimierungen vornimmt, sodass Parameter natürlich auch als Zahlen angezeigt werden). Wenn die $nameVariable im obigen Beispiel 'Sarah'; DELETE FROM employeesdas Ergebnis enthält, wäre dies einfach eine Suche nach der Zeichenfolge "'Sarah'; DELETE FROM employees", und Sie erhalten keine leere Tabelle .

Ein weiterer Vorteil der Verwendung vorbereiteter Anweisungen besteht darin, dass wenn Sie dieselbe Anweisung mehrmals in derselben Sitzung ausführen, sie nur einmal analysiert und kompiliert wird, wodurch Sie einige Geschwindigkeitsgewinne erzielen.

Oh, und da Sie gefragt haben, wie es für eine Einfügung gemacht werden soll, ist hier ein Beispiel (mit PDO):

$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)'); $preparedStatement->execute([ 'column' => $unsafeValue ]);

Können vorbereitete Anweisungen für dynamische Abfragen verwendet werden?

Während Sie weiterhin vorbereitete Anweisungen für die Abfrageparameter verwenden können, kann die Struktur der dynamischen Abfrage selbst nicht parametrisiert werden und bestimmte Abfragefunktionen können nicht parametrisiert werden.

Für diese speziellen Szenarien verwenden Sie am besten einen Whitelist-Filter, der die möglichen Werte einschränkt.

// Value whitelist
// $dir can only be 'DESC', otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
   $dir = 'ASC';
}
1670 MattSheppard Sep 13 2008 at 16:48

Veraltete Warnung: Der Beispielcode dieser Antwort (wie der Beispielcode der Frage) verwendet die PHP- MySQLErweiterung, die in PHP 5.5.0 veraltet und in PHP 7.0.0 vollständig entfernt wurde.

Sicherheitswarnung : Diese Antwort entspricht nicht den bewährten Sicherheitsmethoden. Das Escaping ist nicht ausreichend, um eine SQL-Injection zu verhindern . Verwenden Sie stattdessen vorbereitete Anweisungen . Verwenden Sie die unten beschriebene Strategie auf eigenes Risiko. ( mysql_real_escape_string()Wurde auch in PHP 7 entfernt.)

Wenn Sie eine neuere Version von PHP verwenden, ist die mysql_real_escape_stringunten beschriebene Option nicht mehr verfügbar (obwohl dies mysqli::escape_stringein modernes Äquivalent ist). Heutzutage ist die mysql_real_escape_stringOption nur für Legacy-Code in einer alten Version von PHP sinnvoll.


Sie haben zwei Möglichkeiten - das Ausblenden der Sonderzeichen in Ihrer unsafe_variableoder die Verwendung einer parametrisierten Abfrage. Beides würde Sie vor SQL-Injection schützen. Die parametrisierte Abfrage wird als die bessere Vorgehensweise angesehen, erfordert jedoch den Wechsel zu einer neueren MySQL-Erweiterung in PHP, bevor Sie sie verwenden können.

Wir werden zuerst die untere Aufprallschnur abdecken, die einer entweicht.

//Connect

$unsafe_variable = $_POST["user-input"];
$safe_variable = mysql_real_escape_string($unsafe_variable);

mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

//Disconnect

Siehe auch die Details der mysql_real_escape_stringFunktion.

Um die parametrisierte Abfrage zu verwenden, müssen Sie MySQLi anstelle der MySQL- Funktionen verwenden. Um Ihr Beispiel umzuschreiben, benötigen wir Folgendes.

<?php
    $mysqli = new mysqli("server", "username", "password", "database_name");

    // TODO - Check that connection was successful.

    $unsafe_variable = $_POST["user-input"];

    $stmt = $mysqli->prepare("INSERT INTO table (column) VALUES (?)");

    // TODO check that $stmt creation succeeded // "s" means the database expects a string $stmt->bind_param("s", $unsafe_variable); $stmt->execute();

    $stmt->close(); $mysqli->close();
?>

Die Schlüsselfunktion, die Sie dort nachlesen möchten, wäre mysqli::prepare.

Wie andere vorgeschlagen haben, ist es möglicherweise nützlich / einfacher, eine Abstraktionsebene mit etwas wie PDO zu verstärken .

Bitte beachten Sie, dass der Fall, nach dem Sie gefragt haben, ziemlich einfach ist und dass komplexere Fälle komplexere Ansätze erfordern können. Bestimmtes:

  • Wenn Sie die Struktur von SQL basierend auf Benutzereingaben ändern möchten, helfen parametrisierte Abfragen nicht weiter, und die erforderliche Escape-Funktion wird von nicht abgedeckt mysql_real_escape_string. In diesem Fall ist es besser, wenn Sie die Benutzereingaben über eine Whitelist weiterleiten, um sicherzustellen, dass nur "sichere" Werte zugelassen werden.
  • Wenn Sie in einer Bedingung Ganzzahlen aus Benutzereingaben verwenden und den mysql_real_escape_stringAnsatz wählen, leiden Sie unter dem Problem, das von Polynomial in den Kommentaren unten beschrieben wird. Dieser Fall ist schwieriger, da Ganzzahlen nicht in Anführungszeichen gesetzt werden. Sie können also damit umgehen, indem Sie überprüfen, ob die Benutzereingabe nur Ziffern enthält.
  • Es gibt wahrscheinlich andere Fälle, die mir nicht bekannt sind. Möglicherweise ist dies eine nützliche Ressource für einige der subtileren Probleme, auf die Sie stoßen können.
1087 YourCommonSense Nov 24 2011 at 16:50

Jede Antwort hier deckt nur einen Teil des Problems ab. Tatsächlich gibt es vier verschiedene Abfrageteile, die wir dynamisch zu SQL hinzufügen können:

  • ein Faden
  • eine Zahl
  • eine Kennung
  • ein Syntax-Schlüsselwort

Und vorbereitete Aussagen decken nur zwei davon ab.

Aber manchmal müssen wir unsere Abfrage noch dynamischer gestalten und auch Operatoren oder Bezeichner hinzufügen. Wir brauchen also verschiedene Schutztechniken.

Im Allgemeinen basiert ein solcher Schutzansatz auf einer Whitelist .

In diesem Fall sollte jeder dynamische Parameter in Ihrem Skript fest codiert und aus diesem Satz ausgewählt werden. Zum Beispiel, um eine dynamische Bestellung durchzuführen:

$orders = array("name", "price", "qty"); // Field names $key = array_search($_GET['sort'], $orders)); // if we have such a name
$orderby = $orders[$key]; // If not, first one will be set automatically. $query = "SELECT * FROM `table` ORDER BY $orderby"; // Value is safe

Um den Prozess zu vereinfachen, habe ich eine Whitelist-Hilfsfunktion geschrieben , die alle Aufgaben in einer Zeile erledigt:

$orderby = white_list($_GET['orderby'], "name", ["name","price","qty"], "Invalid field name"); $query  = "SELECT * FROM `table` ORDER BY `$orderby`"; // sound and safe

Es gibt noch einen anderen Weg, um Identifikatoren zu sichern - das Entkommen, aber ich bleibe lieber bei der Whitelist als einem robusteren und expliziteren Ansatz. Solange Sie jedoch eine Kennung in Anführungszeichen haben, können Sie das Anführungszeichen aus Sicherheitsgründen umgehen. Beispielsweise müssen Sie für MySQL standardmäßig das Anführungszeichen verdoppeln, um es zu umgehen . Für andere DBMS wären Escape-Regeln anders.

Dennoch gibt es ein Problem mit SQL - Syntax Schlüsselwort (wie AND, DESCund so weiter ), aber Whitelisting scheint den einzigen Ansatz in diesem Fall.

Eine allgemeine Empfehlung kann also wie folgt formuliert werden

  • Jede Variable, die ein SQL-Datenliteral darstellt (oder einfach ausgedrückt eine SQL-Zeichenfolge oder eine Zahl), muss über eine vorbereitete Anweisung hinzugefügt werden. Keine Ausnahmen.
  • Alle anderen Abfrageteile wie ein SQL-Schlüsselwort, eine Tabelle oder ein Feldname oder ein Operator müssen durch eine weiße Liste gefiltert werden.

Aktualisieren

Obwohl es eine allgemeine Übereinstimmung über die Best Practices für den SQL-Injection-Schutz gibt, gibt es immer noch viele schlechte Praktiken. Und einige von ihnen sind zu tief in den Köpfen der PHP-Benutzer verwurzelt. Zum Beispiel gibt es auf dieser Seite (obwohl für die meisten Besucher unsichtbar) mehr als 80 gelöschte Antworten, die alle von der Community aufgrund schlechter Qualität oder der Förderung schlechter und veralteter Praktiken entfernt wurden. Schlimmer noch, einige der schlechten Antworten werden nicht gelöscht, sondern gedeihen.

Zum Beispiel gibt es (1) (2) immer noch (3) viele (4) Antworten (5) , einschließlich der am zweithäufigsten bewerteten Antwort, die darauf hindeutet, dass Sie manuell Zeichenfolgen entfernen - ein veralteter Ansatz, der sich als unsicher erwiesen hat.

Oder es gibt eine etwas bessere Antwort, die nur eine andere Methode zur Formatierung von Zeichenfolgen vorschlägt und diese sogar als ultimatives Allheilmittel bezeichnet. Während es natürlich nicht ist. Diese Methode ist nicht besser als die normale Formatierung von Zeichenfolgen, behält jedoch alle Nachteile bei: Sie gilt nur für Zeichenfolgen und ist wie jede andere manuelle Formatierung im Wesentlichen eine optionale, nicht obligatorische Maßnahme, die für menschliches Versagen jeglicher Art anfällig ist.

Ich denke, dass dies alles auf einen sehr alten Aberglauben zurückzuführen ist, der von Behörden wie OWASP oder dem PHP-Handbuch unterstützt wird und die Gleichheit zwischen "Entkommen" und Schutz vor SQL-Injektionen proklamiert.

Unabhängig davon, was das PHP-Handbuch seit Ewigkeiten sagt, *_escape_stringmacht es Daten auf keinen Fall sicher und war nie dazu gedacht. Manuelles Escape ist nicht nur für andere SQL-Teile als Zeichenfolgen nutzlos, sondern auch falsch, da es manuell und nicht automatisiert ist.

Und OWASP macht es noch schlimmer, indem es betont, dass Benutzereingaben vermieden werden, was ein völliger Unsinn ist: Im Zusammenhang mit dem Injektionsschutz sollte es keine solchen Worte geben. Jede Variable ist potenziell gefährlich - unabhängig von der Quelle! Mit anderen Worten - jede Variable muss richtig formatiert sein, um in eine Abfrage eingefügt zu werden - unabhängig von der Quelle. Auf das Ziel kommt es an. In dem Moment, in dem ein Entwickler beginnt, die Schafe von den Ziegen zu trennen (er überlegt, ob eine bestimmte Variable "sicher" ist oder nicht), unternimmt er seinen ersten Schritt in Richtung Katastrophe. Ganz zu schweigen davon, dass selbst der Wortlaut darauf hindeutet, dass die Masse am Einstiegspunkt entweicht und der Funktion der magischen Anführungszeichen ähnelt - bereits verachtet, veraltet und entfernt.

Also, im Gegensatz zu, was auch immer „Flucht“, Prepared Statements ist die Maßnahme , dass in der Tat von SQL - Injection schützt (wenn zutreffend).

857 Kibbee Sep 13 2008 at 07:02

Ich würde empfehlen, PDO (PHP Data Objects) zu verwenden, um parametrisierte SQL-Abfragen auszuführen.

Dies schützt nicht nur vor SQL-Injection, sondern beschleunigt auch Abfragen.

Und von PDO mit nicht mysql_, mysqli_und pgsql_Funktionen, machen Sie Ihre Anwendung ein wenig mehr aus der Datenbank abstrahiert, in seltenen Vorkommen , dass Sie Switch - Datenbank - Anbieter haben.

626 Imran Sep 13 2008 at 20:20

Verwenden PDOund vorbereitete Abfragen.

( $connist ein PDOObjekt)

$stmt = $conn->prepare("INSERT INTO tbl VALUES(:id, :name)"); $stmt->bindValue(':id', $id); $stmt->bindValue(':name', $name); $stmt->execute();
556 Zaffy Oct 03 2012 at 21:07

Wie Sie sehen können, schlagen die Leute vor, dass Sie höchstens vorbereitete Aussagen verwenden. Es ist nicht falsch, aber wenn Ihre Abfrage nur einmal pro Prozess ausgeführt wird, würde dies zu einer leichten Leistungsminderung führen.

Ich war mit diesem Problem konfrontiert, aber ich glaube, ich habe es auf sehr raffinierte Weise gelöst - wie Hacker es verwenden, um die Verwendung von Anführungszeichen zu vermeiden. Ich habe dies in Verbindung mit emulierten vorbereiteten Aussagen verwendet. Ich benutze es, um alle Arten von möglichen SQL-Injection-Angriffen zu verhindern .

Mein Ansatz:

  • Wenn Sie erwarten, dass die Eingabe eine Ganzzahl ist, stellen Sie sicher, dass es sich wirklich um eine Ganzzahl handelt. In einer variablen Sprache wie PHP ist dies sehr wichtig. Sie können zum Beispiel diese sehr einfache, aber leistungsstarke Lösung verwenden:sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input);

  • Wenn Sie etwas anderes von Integer Hex erwarten . Wenn Sie es verhexen, entziehen Sie sich perfekt allen Eingaben. In C / C ++ gibt es eine Funktion namens mysql_hex_string(), in PHP können Sie verwenden bin2hex().

    Machen Sie sich keine Sorgen, dass die maskierte Zeichenfolge die doppelte Größe ihrer ursprünglichen Länge hat, denn selbst wenn Sie sie verwenden mysql_real_escape_string, muss PHP dieselbe Kapazität zuweisen ((2*input_length)+1), die dieselbe ist.

  • Diese Hex-Methode wird häufig verwendet, wenn Sie Binärdaten übertragen, aber ich sehe keinen Grund, warum Sie sie nicht für alle Daten verwenden sollten, um SQL-Injection-Angriffe zu verhindern. Beachten Sie, dass Sie Daten mit 0xder MySQL-Funktion voranstellen oder UNHEXstattdessen verwenden müssen.

So zum Beispiel die Abfrage:

SELECT password FROM users WHERE name = 'root';

Wird werden:

SELECT password FROM users WHERE name = 0x726f6f74;

oder

SELECT password FROM users WHERE name = UNHEX('726f6f74');

Hex ist die perfekte Flucht. Keine Möglichkeit zu injizieren.

Unterschied zwischen UNHEX-Funktion und 0x-Präfix

Es gab einige Diskussionen in Kommentaren, deshalb möchte ich es endlich klarstellen. Diese beiden Ansätze sind sehr ähnlich, unterscheiden sich jedoch in einigen Punkten ein wenig:

Das 0xPräfix kann nur für Datenspalten verwendet werden , wie char, varchar, text, block, binary, usw.
Auch seine Verwendung ist ein wenig kompliziert , wenn Sie dabei sind , eine leere Zeichenfolge einzufügen. Sie müssen es vollständig ersetzen, sonst ''wird eine Fehlermeldung angezeigt.

UNHEX()arbeitet an jeder Spalte; Sie müssen sich keine Sorgen um die leere Zeichenfolge machen.


Hex-Methoden werden häufig als Angriffe verwendet

Beachten Sie, dass diese Hex-Methode häufig als SQL-Injection-Angriff verwendet wird, bei dem Ganzzahlen wie Zeichenfolgen sind und nur mit maskiert werden mysql_real_escape_string. Dann können Sie die Verwendung von Anführungszeichen vermeiden.

Zum Beispiel, wenn Sie einfach so etwas tun:

"SELECT title FROM article WHERE id = " . mysql_real_escape_string($_GET["id"])

Ein Angriff kann Sie sehr leicht injizieren . Betrachten Sie den folgenden injizierten Code, der von Ihrem Skript zurückgegeben wird:

SELECT ... WHERE id = -1 UNION ALL SELECT table_name FROM information_schema.tables;

und jetzt einfach die Tabellenstruktur extrahieren:

SELECT ... WHERE id = -1 UNION ALL SELECT column_name FROM information_schema.column WHERE table_name = __0x61727469636c65__;

Und dann wählen Sie einfach die gewünschten Daten aus. Ist es nicht cool?

Wenn der Codierer einer injizierbaren Stelle sie jedoch verhexen würde, wäre keine Injektion möglich, da die Abfrage folgendermaßen aussehen würde:

SELECT ... WHERE id = UNHEX('2d312075...3635');
501 rahularyansharma Jun 17 2011 at 11:00

Veraltete Warnung: Der Beispielcode dieser Antwort (wie der Beispielcode der Frage) verwendet die PHP- MySQLErweiterung, die in PHP 5.5.0 veraltet und in PHP 7.0.0 vollständig entfernt wurde.

Sicherheitswarnung : Diese Antwort entspricht nicht den bewährten Sicherheitsmethoden. Das Escaping ist nicht ausreichend, um eine SQL-Injection zu verhindern . Verwenden Sie stattdessen vorbereitete Anweisungen . Verwenden Sie die unten beschriebene Strategie auf eigenes Risiko. ( mysql_real_escape_string()Wurde auch in PHP 7 entfernt.)

WICHTIG

Der beste Weg, um SQL Injection zu verhindern, besteht darin, vorbereitete Anweisungen zu verwenden, anstatt zu maskieren , wie die akzeptierte Antwort zeigt.

Es gibt Bibliotheken wie Aura.Sql und EasyDB , mit denen Entwickler vorbereitete Anweisungen einfacher verwenden können. Weitere Informationen darüber, warum vorbereitete Anweisungen die SQL-Injection besser stoppen können , finden Sie in diesem mysql_real_escape_string()Bypass und in den kürzlich behobenen Unicode SQL Injection-Schwachstellen in WordPress .

Injektionsprävention - mysql_real_escape_string ()

PHP hat eine speziell entwickelte Funktion, um diese Angriffe zu verhindern. Alles, was Sie tun müssen, ist den Mund voll einer Funktion zu verwenden mysql_real_escape_string.

mysql_real_escape_stringNimmt eine Zeichenfolge, die in einer MySQL-Abfrage verwendet werden soll, und gibt dieselbe Zeichenfolge zurück, wobei alle SQL-Injection-Versuche sicher entkommen sind. Grundsätzlich werden die problematischen Anführungszeichen ('), die ein Benutzer möglicherweise eingibt, durch ein MySQL-sicheres Ersatzzeichen ersetzt, ein Escape-Anführungszeichen \'.

HINWEIS: Sie müssen mit der Datenbank verbunden sein, um diese Funktion nutzen zu können!

// Verbindung zu MySQL herstellen

$name_bad = "' OR 1'"; $name_bad = mysql_real_escape_string($name_bad); $query_bad = "SELECT * FROM customers WHERE username = '$name_bad'"; echo "Escaped Bad Injection: <br />" . $query_bad . "<br />";


$name_evil = "'; DELETE FROM customers WHERE 1 or username = '"; $name_evil = mysql_real_escape_string($name_evil); $query_evil = "SELECT * FROM customers WHERE username = '$name_evil'"; echo "Escaped Evil Injection: <br />" . $query_evil;

Weitere Informationen finden Sie unter MySQL - SQL Injection Prevention .

470 KiranManiya Sep 13 2008 at 07:15

Sie könnten so etwas Grundlegendes tun:

$safe_variable = mysqli_real_escape_string($_POST["user-input"], $dbConnection); mysqli_query($dbConnection, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

Dies wird nicht jedes Problem lösen, aber es ist ein sehr gutes Sprungbrett. Ich habe offensichtliche Punkte wie die Überprüfung der Existenz, des Formats (Zahlen, Buchstaben usw.) der Variablen ausgelassen.

383 Rob Dec 08 2008 at 12:26

Was auch immer Sie letztendlich verwenden, stellen Sie sicher, dass Sie überprüfen, ob Ihre Eingabe noch nicht durch magic_quoteseinen anderen gut gemeinten Müll beschädigt wurde, und führen Sie ihn gegebenenfalls durch stripslashesoder was auch immer, um ihn zu bereinigen.

365 Cedric Jul 04 2011 at 04:50

Veraltete Warnung: Der Beispielcode dieser Antwort (wie der Beispielcode der Frage) verwendet die PHP- MySQLErweiterung, die in PHP 5.5.0 veraltet und in PHP 7.0.0 vollständig entfernt wurde.

Sicherheitswarnung : Diese Antwort entspricht nicht den bewährten Sicherheitsmethoden. Das Escaping ist nicht ausreichend, um eine SQL-Injection zu verhindern . Verwenden Sie stattdessen vorbereitete Anweisungen . Verwenden Sie die unten beschriebene Strategie auf eigenes Risiko. ( mysql_real_escape_string()Wurde auch in PHP 7 entfernt.)

Parametrisierte Abfrage- UND Eingabevalidierung ist der richtige Weg. Es gibt viele Szenarien, in denen SQL-Injection auftreten kann, obwohl mysql_real_escape_string()dies verwendet wurde.

Diese Beispiele sind anfällig für SQL-Injection:

$offset = isset($_GET['o']) ? $_GET['o'] : 0; $offset = mysql_real_escape_string($offset); RunQuery("SELECT userid, username FROM sql_injection_test LIMIT $offset, 10");

oder

$order = isset($_GET['o']) ? $_GET['o'] : 'userid'; $order = mysql_real_escape_string($order); RunQuery("SELECT userid, username FROM sql_injection_test ORDER BY `$order`");

In beiden Fällen können Sie 'die Kapselung nicht schützen.

Quelle : Die unerwartete SQL-Injektion (wenn das Entkommen nicht ausreicht)

314 JohannesFahrenkrug Jul 03 2012 at 17:14

Meiner Meinung nach ist der beste Weg, um SQL-Injection in Ihrer PHP-Anwendung (oder einer anderen Webanwendung) generell zu verhindern, über die Architektur Ihrer Anwendung nachzudenken. Wenn der einzige Weg zum Schutz vor SQL-Injection darin besteht, eine spezielle Methode oder Funktion zu verwenden, die jedes Mal, wenn Sie mit der Datenbank sprechen, das Richtige tut, machen Sie es falsch. Auf diese Weise ist es nur eine Frage der Zeit, bis Sie vergessen, Ihre Abfrage irgendwann in Ihrem Code korrekt zu formatieren.

Die Übernahme des MVC-Musters und eines Frameworks wie CakePHP oder CodeIgniter ist wahrscheinlich der richtige Weg: Allgemeine Aufgaben wie das Erstellen sicherer Datenbankabfragen wurden gelöst und zentral in solchen Frameworks implementiert. Sie helfen Ihnen dabei, Ihre Webanwendung sinnvoll zu organisieren, und lassen Sie mehr über das Laden und Speichern von Objekten nachdenken als über das sichere Erstellen einzelner SQL-Abfragen.

303 ManishShrivastava Jul 23 2012 at 17:19

Es gibt viele Möglichkeiten, SQL-Injektionen und andere SQL-Hacks zu verhindern. Sie können es leicht im Internet finden (Google-Suche). Natürlich ist PDO eine der guten Lösungen. Ich möchte Ihnen jedoch einige gute Links vorschlagen, die die SQL-Injection verhindern sollen.

Was ist SQL-Injection und wie kann dies verhindert werden?

PHP-Handbuch für SQL-Injection

Microsoft-Erklärung zur SQL-Injektion und -Prävention in PHP

Und einige andere wie das Verhindern der SQL-Injektion mit MySQL und PHP .

Nun, warum tun Sie Sie benötigen , um Ihre Abfrage von SQL - Injection zu verhindern?

Ich möchte Sie wissen lassen: Warum versuchen wir anhand eines kurzen Beispiels unten, die SQL-Injection zu verhindern:

Abfrage für Übereinstimmung der Anmeldungsauthentifizierung:

$query="select * from users where email='".$_POST['email']."' and password='".$_POST['password']."' ";

Nun, wenn jemand (ein Hacker) setzt

$_POST['email']= [email protected]' OR '1=1

und Passwort alles ....

Die Abfrage wird nur bis zu:

$query="select * from users where email='[email protected]' OR '1=1';

Der andere Teil wird verworfen. Also, was wird passieren? Ein nicht autorisierter Benutzer (Hacker) kann sich als Administrator anmelden, ohne sein Kennwort zu haben. Jetzt kann er / sie alles tun, was der Administrator / die E-Mail-Person tun kann. Es ist sehr gefährlich, wenn die SQL-Injection nicht verhindert wird.

302 Nikhil Nov 04 2009 at 01:05

Ich bevorzuge gespeicherte Prozeduren ( MySQL unterstützt seit 5.0 gespeicherte Prozeduren ) aus Sicherheitsgründen - die Vorteile sind:

  1. In den meisten Datenbanken (einschließlich MySQL ) kann der Benutzerzugriff auf die Ausführung gespeicherter Prozeduren beschränkt werden. Die fein abgestimmte Sicherheitszugriffskontrolle ist nützlich, um eine Eskalation von Berechtigungsangriffen zu verhindern. Dies verhindert, dass gefährdete Anwendungen SQL direkt für die Datenbank ausführen können.
  2. Sie abstrahieren die SQL-Rohabfrage von der Anwendung, sodass der Anwendung weniger Informationen über die Datenbankstruktur zur Verfügung stehen. Dies erschwert es den Menschen, die zugrunde liegende Struktur der Datenbank zu verstehen und geeignete Angriffe zu entwerfen.
  3. Sie akzeptieren nur Parameter, daher sind die Vorteile parametrisierter Abfragen vorhanden. Natürlich - IMO müssen Sie Ihre Eingabe noch bereinigen - insbesondere, wenn Sie dynamisches SQL in der gespeicherten Prozedur verwenden.

Die Nachteile sind -

  1. Sie (gespeicherte Prozeduren) sind schwer zu warten und vermehren sich sehr schnell. Dies macht die Verwaltung zu einem Problem.
  2. Sie eignen sich nicht sehr gut für dynamische Abfragen. Wenn sie so aufgebaut sind, dass sie dynamischen Code als Parameter akzeptieren, werden viele Vorteile zunichte gemacht.
268 RDK Oct 10 2012 at 21:53

Ich denke, wenn jemand PHP und MySQL oder einen anderen Datenbankserver verwenden möchte:

  1. Denken Sie an das Erlernen von PDO (PHP Data Objects) - es handelt sich um eine Datenbankzugriffsschicht, die eine einheitliche Methode für den Zugriff auf mehrere Datenbanken bietet.
  2. Denken Sie daran, MySQLi zu lernen
  3. Verwenden Sie native PHP-Funktionen wie: strip_tags , mysql_real_escape_string oder einfach , wenn die Variable numerisch ist (int)$foo. Lesen Sie mehr über Art von Variablen in PHP hier . Wenn Sie Bibliotheken wie PDO oder MySQLi verwenden, verwenden Sie immer PDO :: quote () und mysqli_real_escape_string () .

Bibliotheksbeispiele:

---- gU

----- Keine Platzhalter - reif für SQL-Injection! Es ist schlecht

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values ($name, $addr, $city)");

----- Unbenannte Platzhalter

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values (?, ?, ?);

----- Benannte Platzhalter

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) value (:name, :addr, :city)");

--- MySQLi

$request = $mysqliConnection->prepare('
       SELECT * FROM trainers
       WHERE name = ?
       AND email = ?
       AND last_login > ?');

    $query->bind_param('first_param', 'second_param', $mail, time() - 3600);
    $query->execute();

PS :

PDO gewinnt diesen Kampf mit Leichtigkeit. Durch die Unterstützung von zwölf verschiedenen Datenbanktreibern und benannten Parametern können wir den geringen Leistungsverlust ignorieren und uns an die API gewöhnen. Aus Sicherheitsgründen sind beide sicher, solange der Entwickler sie so verwendet, wie sie verwendet werden sollen

Während sowohl PDO als auch MySQLi recht schnell sind, schneidet MySQLi in Benchmarks unwesentlich schneller ab - ~ 2,5% für nicht vorbereitete Anweisungen und ~ 6,5% für vorbereitete.

Und bitte testen Sie jede Abfrage in Ihrer Datenbank - dies ist ein besserer Weg, um eine Injektion zu verhindern.

258 devOp Jun 12 2012 at 15:03

Wenn möglich, geben Sie die Typen Ihrer Parameter ein. Aber es funktioniert nur mit einfachen Typen wie int, bool und float.

$unsafe_variable = $_POST['user_id'];

$safe_variable = (int)$unsafe_variable ;

mysqli_query($conn, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
233 PeterMortensen Oct 15 2012 at 20:52

Wenn Sie Cache-Engines wie Redis oder Memcached nutzen möchten, ist DALMP möglicherweise die richtige Wahl. Es verwendet reines MySQLi . Überprüfen Sie Folgendes : DALMP Database Abstraction Layer für MySQL mit PHP.

Sie können Ihre Argumente auch vorbereiten, bevor Sie Ihre Abfrage vorbereiten, sodass Sie dynamische Abfragen erstellen und am Ende eine vollständig vorbereitete Anweisungsabfrage haben können. DALMP Database Abstraction Layer für MySQL mit PHP.

224 Xeoncross Sep 20 2012 at 01:10

Für diejenigen, die sich nicht sicher sind, wie sie PDO verwenden sollen (aus den mysql_Funktionen), habe ich einen sehr, sehr einfachen PDO-Wrapper erstellt , der eine einzelne Datei ist. Es soll zeigen, wie einfach es ist, alle gängigen Aufgaben zu erledigen. Funktioniert mit PostgreSQL, MySQL und SQLite.

Im Grunde genommen, lesen Sie es , während Sie das Handbuch zu lesen , um zu sehen , wie die PDO - Funktionen im realen Leben Gebrauch zu setzen , um es einfach zu speichern und abzurufen Werte im Format Sie mögen.

Ich möchte eine einzelne Spalte

$count = DB::column('SELECT COUNT(*) FROM `user`);

Ich möchte ein Array (Schlüssel => Wert) Ergebnisse (dh zum Erstellen einer Auswahlbox)

$pairs = DB::pairs('SELECT `id`, `username` FROM `user`);

Ich möchte ein Ergebnis für eine einzelne Zeile

$user = DB::row('SELECT * FROM `user` WHERE `id` = ?', array($user_id));

Ich möchte eine Reihe von Ergebnissen

$banned_users = DB::fetch('SELECT * FROM `user` WHERE `banned` = ?', array(TRUE));
223 NicolasFinelli Aug 04 2012 at 02:59

Mit dieser PHP-Funktion können mysql_escape_string()Sie schnell eine gute Prävention erzielen.

Beispielsweise:

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."'

mysql_escape_string - Entgeht eine Zeichenfolge zur Verwendung in einer mysql_query

Für mehr Prävention können Sie am Ende hinzufügen ...

wHERE 1=1   or  LIMIT 1

Endlich bekommen Sie:

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."' LIMIT 1
221 Danijel May 09 2013 at 23:36

Einige Richtlinien zum Escapezeichen von Sonderzeichen in SQL-Anweisungen.

Verwenden Sie MySQL nicht . Diese Erweiterung ist veraltet. Verwenden Sie stattdessen MySQLi oder PDO .

MySQLi

Zum manuellen Escapezeichen von Sonderzeichen in einer Zeichenfolge können Sie die Funktion mysqli_real_escape_string verwenden. Die Funktion funktioniert nur dann ordnungsgemäß, wenn der richtige Zeichensatz mit mysqli_set_charset festgelegt wurde .

Beispiel:

$mysqli = new mysqli('host', 'user', 'password', 'database'); $mysqli->set_charset('charset');

$string = $mysqli->real_escape_string($string); $mysqli->query("INSERT INTO table (column) VALUES ('$string')");

Verwenden Sie zum automatischen Escapezeichen von Werten mit vorbereiteten Anweisungen mysqli_prepare und mysqli_stmt_bind_param, wobei Typen für die entsprechenden Bindevariablen für eine geeignete Konvertierung angegeben werden müssen:

Beispiel:

$stmt = $mysqli->prepare("INSERT INTO table (column1, column2) VALUES (?,?)"); $stmt->bind_param("is", $integer, $string);

$stmt->execute();

Unabhängig davon, ob Sie vorbereitete Anweisungen verwenden oder mysqli_real_escape_string, müssen Sie immer die Art der Eingabedaten kennen, mit denen Sie arbeiten.

Wenn Sie also eine vorbereitete Anweisung verwenden, müssen Sie die Typen der Variablen für die mysqli_stmt_bind_paramFunktion angeben .

Und die Verwendung von mysqli_real_escape_stringdient dazu, wie der Name schon sagt, Sonderzeichen in einer Zeichenfolge zu maskieren, sodass Ganzzahlen nicht sicher sind. Mit dieser Funktion soll verhindert werden, dass die Zeichenfolgen in SQL-Anweisungen unterbrochen werden und die Datenbank dadurch beschädigt wird. mysqli_real_escape_stringist eine nützliche Funktion, wenn sie richtig verwendet wird, insbesondere in Kombination mit sprintf.

Beispiel:

$string = "x' OR name LIKE '%John%";
$integer = '5 OR id != 0'; $query = sprintf( "SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string($string), $integer); echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 5

$integer = '99999999999999999999'; $query = sprintf("SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string($string), $integer); echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 2147483647
184 ApurvNerlekar Oct 25 2012 at 15:07

Die einfache Alternative zu diesem Problem könnte gelöst werden, indem entsprechende Berechtigungen in der Datenbank selbst erteilt werden. Beispiel: Wenn Sie eine MySQL-Datenbank verwenden, geben Sie die Datenbank über das Terminal oder die bereitgestellte Benutzeroberfläche ein und folgen Sie einfach diesem Befehl:

 GRANT SELECT, INSERT, DELETE ON database TO username@'localhost' IDENTIFIED BY 'password';

Dadurch wird der Benutzer darauf beschränkt, sich nur auf die angegebenen Abfragen zu beschränken. Entfernen Sie die Löschberechtigung, damit die Daten niemals aus der von der PHP-Seite ausgelösten Abfrage gelöscht werden. Als zweites müssen Sie die Berechtigungen leeren, damit MySQL die Berechtigungen und Aktualisierungen aktualisiert.

FLUSH PRIVILEGES; 

Weitere Informationen zu Flush .

Um die aktuellen Berechtigungen für den Benutzer anzuzeigen, lösen Sie die folgende Abfrage aus.

select * from mysql.user where User='username';

Erfahren Sie mehr über GRANT .

177 18revs,8users53%user1646111 Jan 29 2013 at 02:37

In Bezug auf viele nützliche Antworten hoffe ich, diesem Thread einen Mehrwert zu verleihen.

SQL Injection ist ein Angriff, der über Benutzereingaben ausgeführt werden kann (Eingaben, die von einem Benutzer ausgefüllt und dann in Abfragen verwendet werden). Die SQL-Injection-Muster sind die richtige Abfragesyntax, obwohl wir sie als schlechte Abfragen aus schlechten Gründen bezeichnen können, und wir gehen davon aus, dass es möglicherweise eine schlechte Person gibt, die versucht, geheime Informationen zu erhalten (unter Umgehung der Zugriffskontrolle), die die drei Sicherheitsprinzipien (Vertraulichkeit) beeinflussen , Integrität und Verfügbarkeit).

Jetzt geht es darum, Sicherheitsbedrohungen wie SQL-Injection-Angriffe zu verhindern, die Frage (wie man einen SQL-Injection-Angriff mit PHP verhindert) realistischer zu gestalten, Daten zu filtern oder Eingabedaten zu löschen, wenn Benutzereingabedaten im Inneren verwendet werden Eine solche Abfrage mit PHP oder einer anderen Programmiersprache ist nicht der Fall. Wie von mehr Personen empfohlen, moderne Technologien wie vorbereitete Anweisungen oder andere Tools zu verwenden, die derzeit die SQL-Injection-Prävention unterstützen, sind diese Tools nicht mehr verfügbar? Wie sichern Sie Ihre Bewerbung?

Mein Ansatz gegen SQL-Injection lautet: Löschen von Benutzereingabedaten vor dem Senden an die Datenbank (bevor sie in einer Abfrage verwendet werden).

Datenfilterung für (Konvertieren unsicherer Daten in sichere Daten)

Beachten Sie, dass PDO und MySQLi nicht verfügbar sind. Wie können Sie Ihre Bewerbung sichern? Zwingst du mich, sie zu benutzen? Was ist mit anderen Sprachen als PHP? Ich ziehe es vor, allgemeine Ideen zu liefern, da diese für eine breitere Grenze verwendet werden können, nicht nur für eine bestimmte Sprache.

  1. SQL-Benutzer (Einschränkung der Benutzerberechtigung): Die häufigsten SQL-Vorgänge sind (SELECT, UPDATE, INSERT). Warum sollte die UPDATE-Berechtigung dann einem Benutzer erteilt werden, der sie nicht benötigt? Zum Beispiel verwenden Anmelde- und Suchseiten nur SELECT. Warum sollten DB-Benutzer auf diesen Seiten mit hohen Berechtigungen verwendet werden?

REGEL: Erstellen Sie nicht einen Datenbankbenutzer für alle Berechtigungen. Für alle SQL-Vorgänge können Sie Ihr Schema wie (deluser, selectuser, updateuser) als Benutzernamen erstellen, um die Verwendung zu vereinfachen.

Siehe Prinzip des geringsten Privilegs .

  1. Datenfilterung: Bevor eine Abfragebenutzereingabe erstellt wird, sollte diese validiert und gefiltert werden. Für Programmierer ist es wichtig, einige Eigenschaften für jede Benutzereingabevariable zu definieren: Datentyp, Datenmuster und Datenlänge . Ein Feld, das eine Zahl zwischen (x und y) ist, muss anhand der genauen Regel genau validiert werden. Für ein Feld, das eine Zeichenfolge (Text) ist: Muster ist der Fall. Beispielsweise darf ein Benutzername nur einige Zeichen enthalten sagen Sie [a-zA-Z0-9_-.]. Die Länge variiert zwischen (x und n), wobei x und n (ganze Zahlen, x <= n). Regel: Das Erstellen exakter Filter und Validierungsregeln sind für mich Best Practices.

  2. Verwenden Sie andere Tools: Hier werde ich Ihnen auch zustimmen, dass eine vorbereitete Anweisung (parametrisierte Abfrage) und gespeicherte Prozeduren. Die Nachteile hierbei sind, dass diese Methoden fortgeschrittene Fähigkeiten erfordern, die für die meisten Benutzer nicht vorhanden sind. Die Grundidee besteht darin, zwischen der SQL-Abfrage und den darin verwendeten Daten zu unterscheiden. Beide Ansätze können auch mit unsicheren Daten verwendet werden, da die Benutzereingabedaten hier der ursprünglichen Abfrage nichts hinzufügen, wie z. B. (any oder x = x).

Weitere Informationen finden Sie im OWASP SQL Injection Prevention Cheat Sheet .

Wenn Sie ein fortgeschrittener Benutzer sind, können Sie diese Verteidigung nach Belieben verwenden. Für Anfänger ist es jedoch besser, Eingabedaten so weit wie möglich zu filtern, wenn sie eine gespeicherte Prozedur nicht schnell implementieren und die Anweisung vorbereiten können.

Nehmen wir zum Schluss an, dass ein Benutzer diesen Text unten sendet, anstatt seinen Benutzernamen einzugeben:

[1] UNION SELECT IF(SUBSTRING(Password,1,1)='2',BENCHMARK(100000,SHA1(1)),0) User,Password FROM mysql.user WHERE User = 'root'

Diese Eingabe kann frühzeitig ohne vorbereitete Anweisung und gespeicherte Prozeduren überprüft werden. Um jedoch auf der sicheren Seite zu sein, beginnt die Verwendung nach der Filterung und Validierung der Benutzerdaten.

Der letzte Punkt ist das Erkennen unerwarteten Verhaltens, das mehr Aufwand und Komplexität erfordert. Es wird nicht für normale Webanwendungen empfohlen.

Unerwartetes Verhalten in der obigen Benutzereingabe ist SELECT, UNION, IF, SUBSTRING, BENCHMARK, SHA und root. Sobald diese Wörter erkannt wurden, können Sie die Eingabe vermeiden.

UPDATE 1:

Ein Benutzer hat kommentiert, dass dieser Beitrag nutzlos ist, OK! Hier ist , was OWASP.ORG zur Verfügung gestellt :

Primäre Verteidigung:

Option 1: Verwendung vorbereiteter Anweisungen (parametrisierte Abfragen)
Option 2: Verwendung gespeicherter Prozeduren
Option 3: Entkommen aller vom Benutzer bereitgestellten Eingaben

Zusätzliche Verteidigung:

Auch erzwingen: Geringste Berechtigung
Führen Sie auch eine Eingabevalidierung auf der weißen Liste durch

Wie Sie vielleicht wissen, sollte die Behauptung eines Artikels durch ein gültiges Argument gestützt werden, mindestens durch eine Referenz! Ansonsten ist es ein Angriff und eine schlechte Behauptung!

Update 2:

Aus dem PHP-Handbuch, PHP: Prepared Statements - Manual :

Escaping und SQL-Injection

Gebundene Variablen werden vom Server automatisch maskiert. Der Server fügt die Escape-Werte vor der Ausführung an den entsprechenden Stellen in die Anweisungsvorlage ein. Dem Server muss ein Hinweis für den Typ der gebundenen Variablen bereitgestellt werden, um eine geeignete Konvertierung zu erstellen. Weitere Informationen finden Sie in der Funktion mysqli_stmt_bind_param ().

Das automatische Escapezeichen von Werten innerhalb des Servers wird manchmal als Sicherheitsfunktion angesehen, um die SQL-Injection zu verhindern. Das gleiche Maß an Sicherheit kann mit nicht vorbereiteten Anweisungen erreicht werden, wenn Eingabewerte korrekt maskiert werden.

Update 3:

Ich habe Testfälle erstellt, um zu wissen, wie PDO und MySQLi die Abfrage an den MySQL-Server senden, wenn eine vorbereitete Anweisung verwendet wird:

GU:

$user = "''1''"; // Malicious keyword $sql = 'SELECT * FROM awa_user WHERE userame =:username';
$sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY)); $sth->execute(array(':username' => $user));

Abfrageprotokoll:

    189 Query SELECT * FROM awa_user WHERE userame ='\'\'1\'\''
    189 Quit

MySQLi:

$stmt = $mysqli->prepare("SELECT * FROM awa_user WHERE username =?")) { $stmt->bind_param("s", $user); $user = "''1''";
$stmt->execute();

Abfrageprotokoll:

    188 Prepare   SELECT * FROM awa_user WHERE username =?
    188 Execute   SELECT * FROM awa_user WHERE username ='\'\'1\'\''
    188 Quit

Es ist klar, dass eine vorbereitete Anweisung auch den Daten entgeht, sonst nichts.

Wie auch in der obigen Erklärung erwähnt,

Das automatische Escapezeichen von Werten innerhalb des Servers wird manchmal als Sicherheitsfunktion angesehen, um die SQL-Injection zu verhindern. Das gleiche Maß an Sicherheit kann mit nicht vorbereiteten Anweisungen erreicht werden, wenn die Eingabewerte korrekt maskiert werden

Dies beweist daher, dass eine Datenüberprüfung, wie sie beispielsweise intval()für ganzzahlige Werte geeignet ist, vor dem Senden einer Abfrage eine gute Idee ist. Darüber hinaus ist das Verhindern böswilliger Benutzerdaten vor dem Senden der Abfrage ein korrekter und gültiger Ansatz .

Weitere Informationen finden Sie in dieser Frage: PDO sendet eine Rohabfrage an MySQL, während Mysqli eine vorbereitete Abfrage sendet. Beide führen zum gleichen Ergebnis

Verweise:

  1. SQL Injection Cheat Sheet
  2. SQL-Injektion
  3. Informationssicherheit
  4. Sicherheitsgrundsätze
  5. Datenvalidierung
176 SoumalyaBanerjee Sep 14 2012 at 21:37

Sicherheitswarnung : Diese Antwort entspricht nicht den bewährten Sicherheitsmethoden. Das Escaping ist nicht ausreichend, um eine SQL-Injection zu verhindern . Verwenden Sie stattdessen vorbereitete Anweisungen . Verwenden Sie die unten beschriebene Strategie auf eigenes Risiko. ( mysql_real_escape_string()Wurde auch in PHP 7 entfernt.)

Veraltete Warnung : Die MySQL-Erweiterung ist derzeit veraltet. Wir empfehlen die Verwendung der PDO-Erweiterung

Ich verwende drei verschiedene Methoden, um zu verhindern, dass meine Webanwendung für SQL-Injection anfällig ist.

  1. Verwendung von mysql_real_escape_string(), die eine vordefinierte Funktion ist PHP , und dieser Code add umgekehrten Schrägstrichen auf die folgenden Figuren: \x00, \n, \r, \, ', "und \x1a. Übergeben Sie die Eingabewerte als Parameter, um die Wahrscheinlichkeit einer SQL-Injection zu minimieren.
  2. Am weitesten fortgeschritten ist die Verwendung von gU.

Ich hoffe, dies wird dir helfen.

Betrachten Sie die folgende Abfrage:

$iId = mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

mysql_real_escape_string () schützt hier nicht. Wenn Sie in Ihrer Abfrage einfache Anführungszeichen ('') für Ihre Variablen verwenden, schützt dies Sie davor. Hier ist eine Lösung dafür:

$iId = (int) mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

Diese Frage hat einige gute Antworten darauf.

Ich schlage vor, die Verwendung von PDO ist die beste Option.

Bearbeiten:

mysql_real_escape_string()ist ab PHP 5.5.0 veraltet. Verwenden Sie entweder mysqli oder PDO.

Eine Alternative zu mysql_real_escape_string () ist

string mysqli_real_escape_string ( mysqli $link , string $escapestr )

Beispiel:

$iId = $mysqli->real_escape_string("1 OR 1=1");
$mysqli->query("SELECT * FROM table WHERE id = $iId");
170 DeepakThomas Jun 26 2013 at 02:00

Eine einfache Möglichkeit wäre die Verwendung eines PHP-Frameworks wie CodeIgniter oder Laravel, das über integrierte Funktionen wie Filterung und aktive Aufzeichnung verfügt, sodass Sie sich nicht um diese Nuancen kümmern müssen.

147 5ervant Apr 04 2013 at 15:15

Warnung: Der in dieser Antwort beschriebene Ansatz gilt nur für sehr spezifische Szenarien und ist nicht sicher, da SQL-Injection-Angriffe nicht nur auf der Injection-Fähigkeit beruhen X=Y.

Wenn die Angreifer versuchen, sich über die PHP- $_GETVariable oder mit der Abfragezeichenfolge der URL in das Formular zu hacken , können Sie sie abfangen, wenn sie nicht sicher sind.

RewriteCond %{QUERY_STRING} ([0-9]+)=([0-9]+)
RewriteRule ^(.*) ^/track.php

Denn 1=1, 2=2, 1=2, 2=1, 1+1=2, etc ... sind die häufigsten Fragen zu einer SQL - Datenbank eines Angreifers. Vielleicht wird es auch von vielen Hacking-Anwendungen verwendet.

Sie müssen jedoch darauf achten, dass Sie keine sichere Abfrage von Ihrer Site aus neu schreiben. Der obige Code gibt Ihnen einen Tipp, wie Sie diese hacking-spezifische dynamische Abfragezeichenfolge in eine Seite umschreiben oder umleiten können (dies hängt von Ihnen ab) , auf der die IP-Adresse des Angreifers oder AUCH IHRE COOKIES, der Verlauf, der Browser oder andere vertrauliche Informationen gespeichert werden Informationen, damit Sie später mit ihnen umgehen können, indem Sie ihr Konto sperren oder die Behörden kontaktieren.

129 ThomasAhle Mar 20 2014 at 07:17

Eine gute Idee ist es, einen objektrelationalen Mapper wie Idiorm zu verwenden :

$user = ORM::for_table('user') ->where_equal('username', 'j4mie') ->find_one(); $user->first_name = 'Jamie';
$user->save(); $tweets = ORM::for_table('tweet')
    ->select('tweet.*')
    ->join('user', array(
        'user.id', '=', 'tweet.user_id'
    ))
    ->where_equal('user.username', 'j4mie')
    ->find_many();

foreach ($tweets as $tweet) {
    echo $tweet->text;
}

Es erspart Ihnen nicht nur SQL-Injektionen, sondern auch Syntaxfehler! Es werden auch Modellkollektionen mit Methodenverkettung unterstützt, um Aktionen auf mehrere Ergebnisse gleichzeitig und mehrere Verbindungen zu filtern oder anzuwenden.

128 ChintanGor Jan 30 2014 at 14:00

Es gibt so viele Antworten für PHP und MySQL , aber hier ist Code für PHP und Oracle, um die SQL-Injection sowie die regelmäßige Verwendung von oci8-Treibern zu verhindern:

$conn = oci_connect($username, $password, $connection_string);
$stmt = oci_parse($conn, 'UPDATE table SET field = :xx WHERE ID = 123');
oci_bind_by_name($stmt, ':xx', $fieldval);
oci_execute($stmt);
124 RakeshSharma Jan 17 2014 at 13:25

Veraltete Warnung: Der Beispielcode dieser Antwort (wie der Beispielcode der Frage) verwendet die PHP- MySQLErweiterung, die in PHP 5.5.0 veraltet und in PHP 7.0.0 vollständig entfernt wurde.

Sicherheitswarnung : Diese Antwort entspricht nicht den bewährten Sicherheitsmethoden. Das Escaping ist nicht ausreichend, um eine SQL-Injection zu verhindern . Verwenden Sie stattdessen vorbereitete Anweisungen . Verwenden Sie die unten beschriebene Strategie auf eigenes Risiko. ( mysql_real_escape_string()Wurde auch in PHP 7 entfernt.)

Die Verwendung von PDO und MYSQLi ist eine gute Methode , um SQL-Injektionen zu verhindern. Wenn Sie jedoch wirklich mit MySQL-Funktionen und -Abfragen arbeiten möchten, ist es besser, diese zu verwenden

mysql_real_escape_string

$unsafe_variable = mysql_real_escape_string($_POST['user_input']);

Es gibt weitere Möglichkeiten, dies zu verhindern: wie Identifizieren - Wenn die Eingabe eine Zeichenfolge, eine Zahl, ein Zeichen oder ein Array ist, gibt es so viele integrierte Funktionen, um dies zu erkennen. Es ist auch besser, diese Funktionen zum Überprüfen der Eingabedaten zu verwenden.

is_string

$unsafe_variable = (is_string($_POST['user_input']) ? $_POST['user_input'] : '');

is_numeric

$unsafe_variable = (is_numeric($_POST['user_input']) ? $_POST['user_input'] : '');

Und es ist viel besser, diese Funktionen zu verwenden, um Eingabedaten zu überprüfen mysql_real_escape_string.

87 Calmarius Feb 19 2014 at 03:38

Ich habe diese kleine Funktion vor einigen Jahren geschrieben:

function sqlvprintf($query, $args)
{
    global $DB_LINK; $ctr = 0;
    ensureConnection(); // Connect to database if not connected already.
    $values = array(); foreach ($args as $value) { if (is_string($value))
        {
            $value = "'" . mysqli_real_escape_string($DB_LINK, $value) . "'"; } else if (is_null($value))
        {
            $value = 'NULL'; } else if (!is_int($value) && !is_float($value)) { die('Only numeric, string, array and NULL arguments allowed in a query. Argument '.($ctr+1).' is not a basic type, it\'s type is '. gettype($value). '.'); } $values[] = $value; $ctr++;
    }
    $query = preg_replace_callback( '/{(\\d+)}/', function($match) use ($values) { if (isset($values[$match[1]])) { return $values[$match[1]]; } else { return $match[0];
            }
        },
        $query ); return $query;
}

function runEscapedQuery($preparedQuery /*, ...*/) { $params = array_slice(func_get_args(), 1);
    $results = runQuery(sqlvprintf($preparedQuery, $params)); // Run query and fetch results. return $results;
}

Dies ermöglicht das Ausführen von Anweisungen in einem einzeiligen C # -ish String.Format wie:

runEscapedQuery("INSERT INTO Whatever (id, foo, bar) VALUES ({0}, {1}, {2})", $numericVar, $stringVar1, $stringVar2);

Es entweicht unter Berücksichtigung des Variablentyps. Wenn Sie versuchen, Tabellen- und Spaltennamen zu parametrisieren, schlägt dies fehl, da jede Zeichenfolge in Anführungszeichen gesetzt wird. Dies ist eine ungültige Syntax.

SICHERHEITS-UPDATE: In der vorherigen str_replaceVersion wurden Injektionen durch Hinzufügen von {#} Token zu Benutzerdaten zugelassen. Diese preg_replace_callbackVersion verursacht keine Probleme, wenn der Ersatz diese Token enthält.