2769 Stimmen

Wie kann ich SQL-Injection in PHP verhindern?

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

$unsafe_variable = $_POST['user_input']; 

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

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

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

Was kann getan werden, um dies zu verhindern?

182voto

In Anbetracht der vielen nützlichen Antworten hoffe ich, einen Beitrag zu diesem Thema leisten zu können.

SQL-Injection ist ein Angriff, der durch Benutzereingaben (Eingaben, die von einem Benutzer eingegeben und dann in Abfragen verwendet werden) durchgeführt werden kann. Bei den SQL-Injection-Mustern handelt es sich um eine korrekte Abfragesyntax, die wir als schlechte Abfragen aus schlechten Gründen bezeichnen können. Wir gehen davon aus, dass eine böse Person versuchen könnte, geheime Informationen zu erhalten (unter Umgehung der Zugriffskontrolle), die die drei Grundsätze der Sicherheit (Vertraulichkeit, Integrität und Verfügbarkeit) beeinträchtigen.

Nun, unser Punkt ist es, Sicherheitsbedrohungen wie SQL-Injection-Attacken zu verhindern, die Frage zu stellen (wie ein SQL-Injection-Angriff mit PHP zu verhindern), realistischer sein, Datenfilterung oder Clearing-Eingabedaten ist der Fall, wenn mit Benutzer-Eingabe-Daten innerhalb einer solchen Abfrage, mit PHP oder einer anderen Programmiersprache ist nicht der Fall, oder wie von mehr Menschen empfohlen, um moderne Technologie wie Prepared Statement oder andere Tools, die derzeit Unterstützung SQL-Injection-Prävention zu verwenden, bedenken Sie, dass diese Werkzeuge nicht mehr verfügbar? Wie sichern Sie Ihre Anwendung?

Mein Ansatz gegen SQL-Injektion ist: Löschen von Benutzereingabedaten, bevor sie an die Datenbank gesendet werden (bevor sie in einer Abfrage verwendet werden).

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

Bedenken Sie, dass PDO y MySQLi sind nicht verfügbar. Wie können Sie Ihre Anwendung absichern? Zwingen Sie mich, sie zu verwenden? Was ist mit anderen Sprachen als PHP? Ich ziehe es vor, allgemeine Ideen zu liefern, da sie für einen breiteren Rahmen verwendet werden können, nicht nur für eine bestimmte Sprache.

  1. SQL-Benutzer (Einschränkung der Benutzerrechte): Die häufigsten SQL-Operationen sind (SELECT, UPDATE, INSERT), warum also einem Benutzer das UPDATE-Recht geben, der es nicht benötigt? Zum Beispiel, Anmelde- und Suchseiten nur SELECT verwenden, warum dann auf diesen Seiten DB-Benutzer mit hohen Rechten verwenden?

REGEL: Erstellen Sie nicht einen Datenbankbenutzer für alle Berechtigungen. Für alle SQL-Operationen können Sie Ihr Schema wie (deluser, selectuser, updateuser) als Benutzernamen zur einfachen Verwendung erstellen.

Ver Grundsatz des geringsten Rechtsanspruchs .

  1. Datenfilterung: Bevor eine Abfrage erstellt wird, sollten die Benutzereingaben 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 exakt nach der Exakt-Regel validiert werden, und für ein Feld, das eine Zeichenkette (Text) ist: Muster ist der Fall, zum Beispiel, ein Benutzername darf nur einige Zeichen enthalten, sagen wir [a-zA-Z0-9_-.]. Die Länge variiert zwischen (x und n), wobei x und n (ganze Zahlen, x <=n). Regel: Die Erstellung exakter Filter und Validierungsregeln ist für mich die beste Vorgehensweise.

  2. Verwenden Sie andere Werkzeuge: Hier stimme ich Ihnen auch zu, dass eine vorbereitete Anweisung (parametrisierte Abfrage) und gespeicherte Prozeduren. Der Nachteil dabei ist, dass diese Methoden fortgeschrittene Kenntnisse erfordern, die bei den meisten Benutzern nicht vorhanden sind. Der Grundgedanke dabei ist, zwischen der SQL-Abfrage und den Daten, die darin verwendet werden, zu unterscheiden. Beide Ansätze können auch mit unsicheren Daten verwendet werden, da die vom Benutzer eingegebenen Daten nichts zur ursprünglichen Abfrage hinzufügen, wie z. B. (any oder x=x).

Für weitere Informationen lesen Sie bitte OWASP Spickzettel zur Vermeidung von SQL-Injektionen .

Nun, wenn Sie ein fortgeschrittener Benutzer sind, beginnen Sie mit dieser Verteidigung, wie Sie wollen, aber für Anfänger, wenn sie nicht schnell eine gespeicherte Prozedur zu implementieren und die Anweisung vorbereitet, ist es besser, Eingabedaten so viel sie können zu filtern.

Nehmen wir schließlich an, dass ein Benutzer den folgenden Text 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 Eingaben können frühzeitig ohne vorbereitete Anweisungen und gespeicherte Prozeduren überprüft werden, aber um auf der sicheren Seite zu sein, sollten diese erst nach der Filterung und Validierung der Benutzerdaten verwendet werden.

Der letzte Punkt ist die Erkennung von unerwartetem Verhalten, die mehr Aufwand und Komplexität erfordert; sie ist für normale Webanwendungen nicht zu empfehlen.

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

UPDATE 1:

Ein Benutzer kommentierte, dass dieser Beitrag nutzlos ist, OK! Hier ist was OWASP.ORG bietet :

Primäre Verteidigungsmaßnahmen:

Option 1: Verwendung von vorbereiteten Anweisungen (parametrisierte Abfragen)
Option Nr. 2: Verwendung von gespeicherten Prozeduren
Option Nr. 3: Alle vom Benutzer eingegebenen Daten werden unterdrückt

Zusätzliche Verteidigungsmaßnahmen:

Auch durchsetzen: Least Privilege
Auch durchführen: White List Eingabeüberprüfung

Wie Sie vielleicht wissen, sollte die Behauptung eines Artikels durch ein stichhaltiges Argument untermauert werden, zumindest durch eine Referenz! Andernfalls wird es als Angriff und schlechte Behauptung gewertet!

Update 2:

Aus dem PHP-Handbuch, PHP: Vorbereitete Anweisungen - Manual :

Escaping und SQL-Injektion

Gebundene Variablen werden vom Server automatisch escaped. Der Server fügt ihre escapeten Werte an den entsprechenden Stellen in der Anweisungsvorlage vor der Ausführung ein. Es muss ein Hinweis an den Server ein Hinweis auf den Typ der gebundenen Variablen gegeben werden, um eine entsprechende Konvertierung zu erzeugen. Siehe die Funktion mysqli_stmt_bind_param() für weitere Informationen.

Das automatische Escaping von Werten innerhalb des Servers ist manchmal ein Sicherheitsmerkmal, um SQL-Injection zu verhindern. Das gleiche Grad an Sicherheit kann mit nicht vorbereiteten Anweisungen erreicht werden, wenn Eingabewerte korrekt escaped werden.

Update 3:

Ich habe Testfälle erstellt, um herauszufinden, wie PDO und MySQLi die Abfrage an den MySQL-Server senden, wenn ein Prepared Statement verwendet wird:

PDO:

$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));

Abfrage-Log:

    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();

Abfrage-Log:

    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 die Daten auslässt, nichts anderes.

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

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

Dies beweist, dass die Validierung von Daten wie intval() ist eine gute Idee für ganzzahlige Werte, bevor eine Abfrage gesendet wird. Darüber hinaus ist das Verhindern von bösartigen Benutzerdaten vor dem Senden der Abfrage ein korrekter und gültiger Ansatz .

Weitere Einzelheiten finden Sie in dieser Frage: PDO sendet eine rohe Abfrage an MySQL, während Mysqli eine vorbereitete Abfrage sendet; beide liefern das gleiche Ergebnis

Referenzen:

  1. Spickzettel für SQL-Injection
  2. SQL-Einschleusung
  3. Informationssicherheit
  4. Sicherheitsprinzipien
  5. Validierung der Daten

180voto

Soumalya Banerjee Punkte 1963

Sicherheitswarnung : Diese Antwort steht nicht im Einklang mit bewährten Sicherheitsverfahren. Escaping ist unzureichend, um SQL-Injection zu verhindern verwenden vorbereitete Erklärungen stattdessen. Die Anwendung der unten beschriebenen Strategie erfolgt auf eigene Gefahr. (Auch, mysql_real_escape_string() wurde in PHP 7 entfernt).

Veraltete Warnung : Die mysql-Erweiterung ist zur Zeit 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 in PHP und dieser Code fügt Backslashes zu den folgenden Zeichen hinzu: \x00 , \n , \r , \ , ' , " y \x1a . Übergeben Sie die Eingabewerte als Parameter, um die Möglichkeit einer SQL-Injektion zu minimieren.
  2. Die fortschrittlichste Methode ist die Verwendung von PDOs.

Ich hoffe, dies hilft Ihnen.

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() wird hier nicht schützen. Wenn Sie einfache Anführungszeichen (' ') um Ihre Variablen innerhalb Ihrer Abfrage verwenden, schützt Sie das davor. Hier ist eine Lösung für dieses Problem:

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

Diese Frage hat dazu einige gute Antworten.

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

Editar:

mysql_real_escape_string() ist seit 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 )

Ejemplo:

$iId = $mysqli->real_escape_string("1 OR 1=1");
$mysqli->query("SELECT * FROM table WHERE id = $iId");

171voto

Deepak Thomas Punkte 2855

Eine einfache Möglichkeit wäre die Verwendung eines PHP-Frameworks wie CodeIgniter o Laravel die über eingebaute Funktionen wie Filterung und aktive Aufzeichnung verfügen, so dass Sie sich um diese Feinheiten nicht kümmern müssen.

11 Stimmen

Ich denke, der Sinn der Frage besteht darin, dies ohne die Verwendung eines solchen Rahmens zu erreichen.

152voto

Warnung: Der in dieser Antwort beschriebene Ansatz gilt nur für sehr spezielle Szenarien und ist nicht sicher, da SQL-Injection-Angriffe nicht nur auf der Möglichkeit beruhen, die X=Y .

Wenn die Angreifer versuchen, das Formular über die PHP-Funktion $_GET Variable oder mit dem Query-String der URL 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 usw... sind die üblichen Fragen an eine SQL-Datenbank eines Angreifers. Vielleicht wird sie auch von vielen Hacking-Anwendungen verwendet.

Aber Sie müssen aufpassen, dass Sie eine sichere Abfrage nicht von Ihrer Seite aus umschreiben. Der obige Code gibt Ihnen einen Tipp, wie Sie eine Anfrage umschreiben oder umleiten können (das hängt von Ihnen ab) diesen hacking-spezifischen dynamischen Abfrage-String in eine Seite ein, die die Daten des Angreifers speichert IP-Adresse oder sogar ihre COOKIES, ihren Verlauf, ihren Browser oder andere sensible Informationen, damit Sie sich später um sie kümmern können, indem Sie ihr Konto sperren oder die Behörden kontaktieren.

0 Stimmen

@RápliAndrás Eine Art von ([0-9\-]+)=([0-9]+) .

136voto

Thomas Ahle Punkte 29242

Eine gute Idee ist die Verwendung eines objekt-relationaler Mapper wie Idiorm :

$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 bewahrt Sie nicht nur vor SQL-Injektionen, sondern auch vor Syntaxfehlern! Es unterstützt auch Sammlungen von Modellen mit Methodenverkettung zum Filtern oder Anwenden von Aktionen auf mehrere Ergebnisse auf einmal und mehrere Verbindungen.

0 Stimmen

Ich bin ehrlich gesagt mit Ihrem Vorschlag nicht einverstanden. Dies könnte zu einem falschen postiven Gefühl der Sicherheit führen, wenn man irgendein ORM einsetzt. Natürlich kümmern sich die meisten von ihnen um vorbereitete Anweisungen und parametrisierte Abfragen. Ein Neuling, der diesen Beitrag liest, könnte sich immer noch sicher fühlen, wenn er ein beliebiges ORM auswählt - er vertraut ihnen allen. Im Allgemeinen vereinfachen ORM die Dinge, indem sie Implementierungsdetails verstecken/abstrahieren. Sie WOLLEN wirklich prüfen (oder blind vertrauen), wie es gemacht wird. Faustformel: Je größer die Open-Source-Gemeinschaft (Unterstützung) dahinter ist, desto weniger ist es total beschissen ;)

1 Stimmen

Ehrlich gesagt ist es nicht die schlechteste Idee, pocketrocket. Abhängig von der ORM, theres eine sehr sehr hohe Chance, dass die Autoren der ORM ihren Weg um SQL besser als der Programmierer wissen. Es ist ein bisschen wie die alte Regel der Verschlüsselung, die besagt, dass man seine eigene Software nicht entwickeln sollte, wenn man nicht mit seinem Namen auf Forschungspapieren in diesem Bereich vertreten ist, denn die Chancen stehen gut, dass der Angreifer seinen Namen auf Papieren in diesem Bereich hat. Das heißt, wenn es sich um einen ORM handelt, bei dem Sie die gesamte Abfrage oder einen Teil davon eingeben müssen (z. B. Model.filter('where foo = ?',bar)), ist es vielleicht besser, SQL von Hand zu erstellen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X