php - prevention - sql injection verhindern java




Wie kann ich die SQL-Injection in PHP verhindern? (19)

Sicherheitswarnung : Diese Antwort entspricht nicht den bewährten Sicherheitsmethoden. Die Flucht ist nicht ausreichend, um die SQL-Injection zu verhindern , verwenden Sie stattdessen vorbereitete Anweisungen . Verwenden Sie die unten beschriebene Strategie auf eigenes Risiko. (Außerdem wurde mysql_real_escape_string() in PHP 7 entfernt.)

Sie könnten so etwas grundlegendes tun:

$safe_variable = mysql_real_escape_string($_POST["user-input"]);
mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

Dies löst nicht jedes Problem, aber es ist ein sehr guter Sprungbrett. Ich habe offensichtliche Elemente wie das Überprüfen der Existenz der Variablen und des Formats (Zahlen, Buchstaben usw.) ausgelassen.

https://code.i-harness.com

Wenn Benutzereingaben ohne Änderung in eine SQL-Abfrage eingefügt werden, wird die Anwendung für SQL-Injection anfällig, 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 wie value'); DROP TABLE table;-- eingeben kann value'); DROP TABLE table;-- value'); DROP TABLE table;-- , und die Abfrage wird zu:

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

Was kann getan werden, um dies zu verhindern?


WICHTIG

Die beste Möglichkeit, SQL Injection zu verhindern, ist die Verwendung von Prepared Statements anstelle von Escaping , wie die akzeptierte Antwort zeigt.

Es gibt Bibliotheken wie Aura.Sql und EasyDB , mit denen Entwickler vorbereitete Anweisungen einfacher verwenden können. Weitere Informationen dazu, warum vorbereitete Anweisungen die SQL-Injection besser stoppen können , finden Sie in diesem mysql_real_escape_string() Umgehung von mysql_real_escape_string() und vor kurzem 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 einer Funktion, mysql_real_escape_string .

mysql_real_escape_string nimmt eine Zeichenfolge, die in einer MySQL-Abfrage verwendet wird, und gibt dieselbe Zeichenfolge mit allen SQL-Injection-Versuchen zurück, die sicher mit Escapezeichen versehen sind. Im Wesentlichen werden die problematischen Anführungszeichen ('), die ein Benutzer eingeben kann, durch einen MySQL-sicheren Ersatz ersetzt, ein Escape-Zitat \'.

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

// Verbinden Sie sich mit MySQL

$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 in MySQL - SQL Injection Prevention .


Die parametrisierte Abfrage- und Eingabevalidierung ist der Weg zu gehen. Es gibt viele Szenarien, unter denen die SQL-Injection auftreten kann, obwohl mysql_real_escape_string() 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 ' nicht verwenden ' um die Kapselung zu schützen.

Source : Die unerwartete SQL-Injektion (wenn die Flucht nicht ausreichend ist)


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.

Durch die Verwendung von mysql_ anstelle von mysql_ , mysqli_ und pgsql_ Funktionen machen Sie Ihre App etwas mehr von der Datenbank abstrahiert, in dem seltenen mysqli_ , dass Sie den Datenbankanbieter wechseln müssen.


Meines Erachtens ist es am besten, die SQL-Injection in Ihrer PHP-Anwendung (oder auch einer Webanwendung) generell zu verhindern, indem Sie über die Architektur Ihrer Anwendung nachdenken. Wenn Sie sich nur vor einer SQL-Injection schützen können, ist die Verwendung einer speziellen Methode oder Funktion, die The Right Thing jedes Mal ausführt, wenn Sie mit der Datenbank sprechen, dass Sie dies falsch machen. Auf diese Weise ist es nur eine Frage der Zeit, bis Sie vergessen, Ihre Abfrage irgendwann in Ihrem Code richtig zu formatieren.

Die Übernahme des MVC-Musters und eines Frameworks wie CakePHP oder CodeIgniter ist wahrscheinlich der richtige Weg: Häufige Aufgaben wie das Erstellen sicherer Datenbankabfragen wurden gelöst und zentral in solchen Frameworks implementiert. Sie helfen Ihnen dabei, Ihre Webanwendung auf sinnvolle Art und Weise zu organisieren und machen Ihnen mehr Gedanken zum Laden und Speichern von Objekten als zum sicheren Erstellen einzelner SQL-Abfragen.


Verwenden Sie PDO und vorbereitete Abfragen.

( $conn ist ein PDO Objekt)

$stmt = $conn->prepare("INSERT INTO tbl VALUES(:id, :name)");
$stmt->bindValue(':id', $id);
$stmt->bindValue(':name', $name);
$stmt->execute();

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

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

Mein Ansatz:

  • Wenn Sie erwarten, dass die Eingabe eine Ganzzahl ist, stellen Sie sicher, dass sie wirklich ganzzahlig ist. In einer Sprache mit variablen Typen wie PHP ist dies sehr wichtig. Sie können beispielsweise diese sehr einfache, aber leistungsfähige 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 jeder Eingabe. In C / C ++ gibt es eine Funktion namens mysql_hex_string() , in PHP können Sie bin2hex() .

    Machen Sie sich keine Sorgen, dass der maskierte String eine doppelte Größe der ursprünglichen Länge hat, da PHP selbst bei Verwendung von mysql_real_escape_string dieselbe Kapazität ((2*input_length)+1) , was dieselbe ist.

  • Diese Hex-Methode wird häufig verwendet, wenn Sie binäre Daten übertragen. Ich sehe jedoch keinen Grund, warum Sie sie nicht für alle Daten verwenden sollten, um SQL-Injection-Angriffe zu verhindern. Beachten Sie, dass Sie Daten mit 0x voranstellen müssen oder stattdessen die MySQL-Funktion UNHEX verwenden.

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 spritzen.

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

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

Das Präfix ** 0x ** kann nur für Datenspalten wie char, varchar, text, block, binary usw. verwendet werden .
Die Verwendung ist etwas kompliziert, wenn Sie gerade eine leere Zeichenkette einfügen. Sie müssen es vollständig durch '' ersetzen, oder Sie erhalten eine Fehlermeldung.

UNHEX () funktioniert in jeder Spalte. Sie müssen sich nicht um die leere Zeichenfolge kümmern.

Hex-Methoden werden oft als Angriffe verwendet

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

Wenn Sie zum Beispiel so etwas tun:

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

ein Angriff kann Sie sehr leicht spritzen. Betrachten Sie den folgenden eingefügten Code, der von Ihrem Skript zurückgegeben wurde:

SELECT ... WHERE id = -1 union all select table_name aus information_schema.tables

und extrahieren Sie jetzt einfach die Tabellenstruktur:

SELECT ... WHERE id = -1 union all select spaltenname aus information_schema.column mit tabellenname = 0x61727469636c65

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

Wenn der Codierer einer injizierbaren Stelle jedoch hexen würde, wäre keine Injektion möglich, da die Abfrage folgendermaßen aussehen würde: SELECT ... WHERE id = UNHEX('2d312075...3635')


Verwenden Sie vorbereitete Anweisungen und parametrisierte Abfragen. Dies sind SQL-Anweisungen, die getrennt von Parametern an den Datenbankserver gesendet und dort analysiert werden. Auf diese Weise ist es einem Angreifer unmöglich, schädliches SQL einzubringen.

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

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

    $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
    
    $stmt->execute(array('name' => $name));
    
    foreach ($stmt as $row) {
        // do something with $row
    }
    
  2. Verwendung 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 eine Verbindung zu einer anderen Datenbank als MySQL herstellen, gibt es eine pg_prepare() zweite Option, auf die Sie sich beziehen können (z. B. pg_prepare() und pg_execute() für PostgreSQL). PDO ist die universelle Option.

Verbindung richtig einrichten

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

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

$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 stoppt das Skript nicht mit einem Fatal Error wenn etwas schief geht. Und es gibt dem Entwickler die Möglichkeit, alle Fehler PDOException throw n als PDOException s PDOException .

setAttribute() ist jedoch die erste setAttribute() Zeile, die PDO setAttribute() , 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 (ein möglicher Angreifer hat also keine Möglichkeit, schädliches SQL einzubringen).

Obwohl Sie den charset in den Optionen des Konstruktors festlegen können, ist zu beachten, dass ältere Versionen von PHP (<5.3.6) den Zeichensatzparameter im DSN ignoriert haben .

Erläuterung

Was passiert, ist, dass die zur prepare SQL-Anweisung vom Datenbankserver analysiert und kompiliert wird. Durch die Angabe von Parametern (entweder ein ? Oder ein benannter Parameter wie :name im obigen Beispiel) geben Sie der Datenbank-Engine an, nach welcher Datenbank sie filtern soll. Beim Aufruf von execute wird die vorbereitete Anweisung dann mit den von Ihnen angegebenen Parameterwerten kombiniert.

Wichtig ist hierbei, dass die Parameterwerte mit der kompilierten Anweisung und nicht mit einer SQL-Zeichenfolge kombiniert werden. Die SQL-Injection funktioniert, indem das Skript dazu verleitet wird, schädliche Zeichenfolgen einzuschließen, wenn SQL zum Senden an die Datenbank erstellt wird. Indem Sie also die eigentliche SQL getrennt von den Parametern senden, begrenzen Sie das Risiko, etwas zu enden, das Sie nicht beabsichtigt haben. Alle Parameter, die Sie senden, wenn Sie eine vorbereitete Anweisung verwenden, werden nur als Zeichenfolgen behandelt (obwohl das Datenbankmodul möglicherweise einige Optimierungen durchführt, sodass die Parameter natürlich auch als Zahlen enden). Im obigen Beispiel, wenn die Variable $name 'Sarah'; DELETE FROM employees enthält 'Sarah'; DELETE FROM employees 'Sarah'; DELETE FROM employees Das Ergebnis wäre einfach eine Suche nach der Zeichenfolge "'Sarah'; DELETE FROM employees" , und Sie werden nicht mit einer leeren Tabelle enden.

Ein weiterer Vorteil der Verwendung von vorbereiteten Anweisungen ist, dass wenn Sie dieselbe Anweisung viele Male in derselben Sitzung ausführen, diese nur einmal analysiert und kompiliert wird, wodurch Sie einige Geschwindigkeitsgewinne erzielen.

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

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

$preparedStatement->execute(array('column' => $unsafeValue));

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

Sie können zwar immer noch vorbereitete Anweisungen für die Abfrageparameter verwenden, die Struktur der dynamischen Abfrage selbst kann jedoch nicht parametrisiert werden, und bestimmte Abfragefunktionen können nicht parametrisiert werden.

Für diese speziellen Szenarien ist es am besten, einen Whitelist-Filter zu verwenden, 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';
}

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

  1. Überlegen Sie sich, PDO (PHP Data Objects) zu lernen - es handelt sich um eine Datenbankzugriffsschicht, die eine einheitliche Zugriffsmethode auf mehrere Datenbanken bietet.
  2. Denken Sie darüber nach, MySQLi lernenMySQLi
  3. Verwenden Sie native PHP-Funktionen wie: strip_tags , mysql_real_escape_string() oder wenn die Variable numerisch ist (int)$foo. Lesen Sie mehr über Art von Variablen in PHP here . 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 :

Die 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 seine API gewöhnen. Unter Sicherheitsaspekten sind beide sicher, solange der Entwickler sie so verwendet, wie sie verwendet werden sollen

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

Testen Sie bitte jede Abfrage in Ihrer Datenbank. Dies ist eine bessere Methode, um die Injektion zu verhindern.


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 entgeht dem Variablentyp. 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: Die vorherige str_replaceVersion erlaubte das Hinzufügen von {#} -Token zu den Benutzerdaten. Diese preg_replace_callbackVersion verursacht keine Probleme, wenn der Ersatz diese Token enthält.


** Warnung: Der in dieser Antwort beschriebene Ansatz gilt nur für sehr spezifische Szenarien und ist nicht sicher, da SQL-Injection-Angriffe nicht nur von Injektionen abhängig sind 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 fangen, 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 vorsichtig sein, dass Sie eine sichere Abfrage von Ihrer Site nicht neu schreiben dürfen. Der obige Code gibt Ihnen den Tipp, diese hacking-spezifische dynamische Abfragezeichenfolge in eine Seite zu schreiben , von der die IP-Adresse des Angreifers gespeichert wird , oder von EVE IHRE COOKIES, dem Verlauf, dem Browser oder anderen sensiblen Objekten (je nach Ihnen) Informationen, damit Sie später mit ihnen umgehen können, indem Sie ihr Konto sperren oder Behörden kontaktieren.


Die Verwendung von PDO und MYSQLi ist eine bewährte MYSQLi , um SQL-Injektionen zu verhindern. Wenn Sie jedoch wirklich mit MySQL-Funktionen und MYSQLi arbeiten möchten, sollten Sie dies besser MYSQLi

mysql_real_escape_string()

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

Es gibt weitere Möglichkeiten, dies zu verhindern: Identifizieren - Wenn die Eingabe eine Zeichenfolge, eine Zahl, ein Zeichen oder ein Array ist, gibt es so viele eingebaute Funktionen, um dies zu erkennen. Es ist auch besser, diese Funktionen zur Überprüfung der Eingangsdaten 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 zur Überprüfung von Eingabedaten zu verwenden mysql_real_escape_string.


Ein einfacher Weg wäre die Verwendung eines PHP-Frameworks wie CodeIgniter oder Laravel das über integrierte Funktionen wie Filterung und Active-Record verfügt, sodass Sie sich nicht um diese Nuancen kümmern müssen.


Eine gute Idee ist die Verwendung eines 'objektrelationalen 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 erspart Ihnen nicht nur SQL-Injektionen, sondern auch Syntaxfehler! Unterstützt auch Modellsammlungen mit Methodenverkettung zum Filtern oder Anwenden von Aktionen auf mehrere Ergebnisse gleichzeitig und mehrere Verbindungen.


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. Aber ich möchte Ihnen einige gute Links vorbeugen, die durch SQL Injection verhindert werden.

Was ist SQL-Injection und wie kann man das verhindern?

PHP-Handbuch zur SQL-Injection

Microsoft Erklärung der SQL-Injection und -Vermeidung in PHP

und einige andere wie SQL-Injection mit MySQL und PHP verhindern

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

Ich möchte Sie wissen lassen: Warum versuchen wir die SQL-Injektion mit einem kurzen Beispiel zu verhindern:

Abfrage zur Übereinstimmung der Anmeldeauthentifizierung:

$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. Was wird also passieren? Ein nicht autorisierter Benutzer (Hacker) kann sich ohne sein Passwort als Administrator anmelden. Jetzt kann er alles tun, was der Administrator / E-Mail-Mitarbeiter tun kann. Es ist sehr gefährlich, wenn die SQL-Injection nicht verhindert wird.


Für diejenigen, die nicht sicher sind, wie man PDO verwendet (von den mysql_Funktionen kommt), habe ich einen sehr einfachen PDO-Wrapper erstellt , der eine einzige Datei ist. Es gibt zu zeigen, wie einfach es ist, all die üblichen Dinge zu erledigen, in denen Anwendungen ausgeführt werden müssen. Funktioniert mit PostgreSQL, MySQL und SQLite.

Im Grunde genommen, lesen Sie es , PDO , um zu sehen , wie die PDO - Funktionen setzen im wirklichen Leben zu verwenden , um es einfach zu speichern und abzurufen Werte im Format Sie mögen.

Ich möchte eine einzelne Kolumne

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

Ich möchte ein Array (key => value) ergebnisse (zB für eine selectbox)

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

Ich möchte ein einziges Reihenergebnis

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

In Bezug auf viele nützliche Antworten hoffe ich, diesem Thread einige Werte hinzufügen zu können. Die SQL-Injection ist ein Angriff, der durch Benutzereingaben (Eingaben, die vom Benutzer ausgefüllt und dann in Abfragen verwendet werden) ausgeführt werden kann. Die SQL-Injection-Muster sind korrekte Abfragesyntax, während wir sie aufrufen können: schlechte Abfragen aus schlechten Gründen, wir gehen davon aus, dass dies möglich ist Seien Sie eine schlechte Person, die versucht, (unter Umgehung der Zugangskontrolle) geheime Informationen zu erhalten, die die drei Sicherheitsgrundsätze (Vertraulichkeit, Integrität, Verfügbarkeit) betreffen.

Nun ist es unser Ziel, Sicherheitsbedrohungen wie SQL-Injection-Angriffe zu verhindern, die Frage zu stellen (Wie verhindert man SQL-Injection-Angriffe mit PHP), realistischer zu sein Abfragen, Verwenden von PHP oder einer anderen Programmiersprache ist nicht der Fall oder, wie von mehreren Leuten empfohlen, moderne Technologien wie vorbereitete Anweisungen oder andere Tools zu verwenden, die derzeit den SQL-Injektionsschutz unterstützen, sind diese Tools nicht mehr verfügbar? Wie sichern Sie Ihre Bewerbung?

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

Datenfilterung für (Konvertieren unsicherer Daten in sichere Daten) MySQLi Sie, dass PDO und MySQLi nicht verfügbar sind. Wie können Sie Ihre Anwendung MySQLi ? Zwingst du mich, sie zu benutzen? Was ist mit anderen Sprachen als PHP? Ich ziehe es vor, allgemeine Ideen zur Verfügung zu stellen, da sie nicht nur für eine bestimmte Sprache, sondern für eine breitere Grenze verwendet werden können.

  1. SQL-Benutzer (Einschränkung des Benutzerprivilegs): Die gebräuchlichsten SQL-Operationen sind (SELECT, UPDATE, INSERT). Zum Beispiel verwenden Anmeldeseiten und Suchseiten nur SELECT. Warum also DB-Benutzer auf diesen Seiten mit hohen Berechtigungen verwenden? RULE: 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 für eine einfache Verwendung erstellen.

siehe Prinzip des geringsten Privilegs

  1. Datenfilterung: Bevor eine Abfrage erstellt werden kann, sollten Benutzereingaben überprüft und gefiltert werden. Für Programmierer müssen einige Eigenschaften für die einzelnen Benutzereingabevariablen definiert werden: Datentyp, Datenmuster und Datenlänge . Ein Feld, das eine Zahl zwischen (x und y) ist, muss mithilfe einer genauen Regel genau überprüft werden. Für ein Feld, das eine Zeichenfolge (Text) ist: Muster ist der Fall. Beispiel: username darf nur einige Zeichen enthalten. 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 ist für mich die beste Praxis.

  2. Verwenden Sie andere Tools: Hier stimme ich Ihnen auch zu, dass die vorbereitete Anweisung (parametrisierte Abfrage) und gespeicherte Prozeduren die Nachteile hier sind. Diese Möglichkeiten erfordern fortgeschrittene Fähigkeiten, die für die meisten Benutzer nicht vorhanden sind. Die Grundidee hier ist, zwischen SQL-Abfragen zu unterscheiden und die Daten, die darin verwendet werden, können beide Ansätze auch mit unsicheren Daten verwendet werden, da die vom Benutzer eingegebenen Daten der ursprünglichen Abfrage nichts hinzufügen, wie (any oder x = x). Weitere Informationen finden Sie im OWASP SQL Injection Prevention-Datenblatt .

Wenn Sie ein fortgeschrittener Benutzer sind, können Sie diese Verteidigung jetzt wie gewünscht verwenden. Wenn Anfänger jedoch die gespeicherte Prozedur nicht schnell implementieren und die Anweisung vorbereiten können, ist es besser, die Eingabedaten nach Möglichkeit zu filtern.

Lassen Sie uns schließlich berücksichtigen, dass der 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 Eingabe kann frühzeitig ohne vorbereitete Anweisungen und gespeicherte Prozeduren überprüft werden. Um auf der sicheren Seite zu sein, beginnt deren Verwendung jedoch nach der Filterung und Validierung der Benutzerdaten.

Der letzte Punkt ist das Erkennen eines 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, root.

UPDATE1:

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

Hauptabwehrmaßnahmen:

Option # 1: Verwendung vorbereiteter Anweisungen (parametrisierte Abfragen)
Option # 2: Verwendung gespeicherter Prozeduren
Option # 3: Alle vom Benutzer bereitgestellten Eingaben übermitteln

Zusätzliche Abwehrmaßnahmen:

Erzwingen: Wenig Berechtigung
Führen Sie außerdem Folgendes aus: Überprüfung der Whitelist- Eingabe

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

Update2:

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

Flucht und SQL-Injection

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

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

Update3:

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

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

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 Anweisung erwähnt The automatic escaping of values within the server is sometimes considered a security feature to prevent SQL injection. The same degree of security can be achieved with non-prepared statements, if input values are escaped correctly, beweist dies, dass die Datenvalidierung, wie z. B. intval()für Integer-Werte eine gute Idee ist, bevor eine Abfrage gesendet wird, und dass bösartige Benutzerdaten vor dem Senden der Abfrage verhindert werden, dass sie korrekt und gültig sind .

Weitere Informationen finden Sie in dieser Frage: PDO sendet eine Rohabfrage an MySQL, während Mysqli eine vorbereitete Abfrage sendet. Beide Ergebnisse liefern das gleiche Ergebnis

Verweise:

  1. SQL Injection-Spickzettel
  2. SQL-Injektion
  3. Informationssicherheit
  4. Sicherheitsgrundsätze
  5. Datenvalidierung

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

Zum Beispiel:

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

mysql_escape_string - Escape ein String 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 bekommst du:

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

Wenn möglich, geben Sie die Typen Ihrer Parameter an. Es funktioniert jedoch 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 . "')");




sql-injection