bist du verrückt? HP mit Sicherheitslücken und alles liegt offen, jedes scriptkiddy kann deine datenbank jetzt plätten
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Standardantwort/FAQ:
Dein Code ist anfällig für SQL Injections. Das heißt, dass ein Angreifer möglicherweise in der Lage ist beliebigen Code in deiner Datenbank auszuführen(Passwörter auslesen etc.)
Verhindern kannst du das, indem du bestimmte Zeichen das Escapezeichen "\" vorranstellt. Für MySQL erledigt das die Funktion
DOKU-VORLESE-SERVICE(TM)
string mysql_real_escape_string
Quote:
mysql_real_escape_string
(PHP 4 >= 4.3.0, PHP 5)
mysql_real_escape_string -- Maskiert spezielle Zeichen innerhalb eines Strings für die Verwendung in einer SQL-Anweisung
Beschreibung
string mysql_real_escape_string ( string unescaped_string [, resource link_identifier] )
Maskiert spezielle Zeichen im unescaped_string unter Berücksichtigung des aktuellen Zeichensatzes der Verbindung, so dass das Ergebnis ohne Probleme in mysql_query() verwendet werden kann. Wenn Sie Binärdaten einfügen wollen, müssen Sie die Funktion auf jeden Fall verwenden.
mysql_real_escape_string() ruft die Funktion mysql_real_escape_string der MySQL-Bibliothek auf, die folgende Zeichen mit einem Backslash ('\') versieht: \x00, \n, \r, \, ', " und \x1a.
Die Funktion muss immer (mit wenigen Ausnahmen) verwendet werden, um Daten abzusichern, bevor sie per Query an MySQL übermittelt werden.
Parameter Liste
unescaped_string
Der zu maskierende String.
Verbindungs-Kennung
Die MySQL-Verbindung. Wird die Verbindungskennung nicht angegeben, wird die letzte durch mysql_connect() geöffnete Verbindung angenommen. Falls keine solche Verbindung gefunden wird, wird versucht, eine Verbindung aufzubauen, wie es beim Aufruf von mysql_connect() ohne Angabe von Argumenten der Fall wäre. Falls zufällig keine Verbindung gefunden oder aufgebaut werden kann, wird eine Warnung der Stufe E_WARNING erzeugt.
Rückgabewerte
Gibt einen maskierten String oder im Fehlerfall FALSE zurück.
Beispiele
Beispiel 1. Einfaches mysql_real_escape_string()-Beispiel
PHP Code:
<?php
// Verbindung herstellen
$link = mysql_connect('mysql_host', 'mysql_user', 'mysql_password')
OR die(mysql_error());
// Anfrage erstellen
$query = sprintf("SELECT * FROM users WHERE user='%s' AND password='%s'",
mysql_real_escape_string($user),
mysql_real_escape_string($password));
?>
Beispiel 2. Ein beispielhafter SQL Injection Angriff
PHP Code:
<?php
// Datenbankabfrage zur Ueberpruefung der Logindaten
$query = "SELECT * FROM users WHERE user='{$_POST['username']}' AND password='{$_POST['password']}'";
mysql_query($query);
// Wir haben $_POST['password'] nicht geprueft, es koennte also alles darin
// stehen, was der User will. Zum Beispiel:
$_POST['username'] = 'aidan';
$_POST['password'] = "' OR ''='";
// Das bedeutet, der an MySQL gesendete Query wuerde sein:
echo $query;
?>
Die Abfrage, die an MySQL übermittelt wird:
Code:
SELECT * FROM users WHERE user='aidan' AND password='' OR ''=''
Dies würde jedermann erlauben, sich ohne valides Passwort einzuloggen.
Beispiel 3. Optimale Vorgehensweise zur Querybehandlung
Die Verwendung von mysql_real_escape_string() bei jeder Variablen beugt SQL Injection Angriffen vor. Das Beispiel demonstriert ein optimales Verfahren für Datenbankanfragen, das unabhängig vom für Magic Quotes gesetzten Wert funktioniert.
PHP Code:
<?php
if (isset($_POST['product_name'])
&& isset($_POST['product_description'])
&& isset($_POST['user_id'])) {
// Verbinden mit der Datenbank
$link = mysql_connect('mysql_host', 'mysql_user', 'mysql_password')
if(!is_resource($link)) {
echo "Verbindung zum Server fehlgeschlagen\n";
// ... den Fehler loggen
} else {
// Die Auswirkungen von magic_quotes_gpc zurücksetzen, sofern die
// Option eingeschaltet ist
if(get_magic_quotes_gpc()) {
$product_name = stripslashes($_POST['product_name']);
$product_description = stripslashes($_POST['product_description']);
} else {
$product_name = $_POST['product_name'];
$product_description = $_POST['product_description'];
}
// einen sicheren Query zusammenstellen
$query = sprintf("INSERT INTO products (`name`, `description`, `user_id`) VALUES ('%s', '%s', '%d')",
mysql_real_escape_string($product_name, $link),
mysql_real_escape_string($product_description, $link),
$_POST['user_id']);
mysql_query($query, $link);
if (mysql_affected_rows($link) > 0) {
echo "Produkt eingefuegt\n";
}
}
} else {
echo "Fuellen Sie das Formular korrekt aus.\n";
}
?>
Die Anfrage wird jetzt korrekt ausgeführt und SQL Injection Angriffe funktionieren nicht mehr.
Anmerkungen
Anmerkung: Sie müssen eine Verbindung zu MySQL geöffnet haben, bevor Sie mysql_real_escape_string() verwenden, ansonsten erhalten Sie einen Fehler vom Typ E_WARNING und der Rückgabewert wird zu FALSE. Ist link_identifier nicht angegeben, wird die letzte MySQL-Verbindung verwendet.
Anmerkung: Ist magic_quotes_gpc aktiviert, wenden Sie zuerst stripslashes() auf die Daten an. Das Bearbeiten bereits in irgend einer Form maskierter Daten durch mysql_real_escape_string führt ansonsten dazu, dass bereits Maskiertes doppelt maskiert wird.
Anmerkung: Wenn die Funktion nicht verwendet wird, um die Daten zu maskieren, ist der Query anfällig für SQL Injection Angriffe.
Anmerkung: mysql_real_escape_string() maskiert weder % noch _. Diese Zeichen werden in MySQL als Platzhalter interpretiert, wenn sie mit LIKE, GRANT oder REVOKE kombiniert werden.
|
(string unescaped_string[, resource Ergebnis-Kennung] )
Escapes special characters in a string for use in a SQL statement, taking into account the current charset of the connection.
. Es sollten ziemlich alle Benutzereingaben(z.B. aus einem Formular oder aus der URL), die in SQL Queries verwendet werden auf diese Weise gefiltert werden.
Im p h p f o r u m . d e Wiki gibt es einen Artikel zu SQL Injections mit einem konkreten Beispiel:
SQL Injections
Quote:
Das Problem
Eine Reihe von Zeichen verursacht Probleme, wenn man sie unverändert in ein SQL-Statement einbaut. Man muss deshalb zuerst diese Zeichen "escapen". Vergisst man diesen Schritt, dann wird das Skript zur Zielscheibe für SQL-Injektionsangriffe.
(Die Beispiele in diesem Artikel sind alle MySQL-spezifisch, aber das Problem betrifft sämtliche Datenbankschnittstellen.)
Ein Beispiel für einen Angriff
Nehmen wir mal an, eine Site enthält einen geschützten Bereich, für den man sich einloggen muss. Ein schlecht programmiertes Skript fuer das Login-Formular koennte z.B. so aussehen:
Warnung:
DER FOLGENDE CODE IST GEFÄHRLICH!
PHP Code:
$sql = 'SELECT * FROM benutzer WHERE
user = "' . $_POST['user'] . '" AND
pass = "' . $_POST['pass'] . '"';
$result = mysql_query($sql);
if (mysql_num_rows($result)) {
// Ja, der Benutzer ist angemeldet und hat Zugang
} else {
// Nein, der Benutzer existiert nicht
// oder hat Name oder Passwort falsch eingegeben
}
Ein Angreifer könnte diesen Fehler ausbeuten, indem er als Passwort
Code:
Abrakadabra" OR ""="
eingibt. Damit wird aus dem SQL-Statement:
Code:
SELECT * FROM benutzer WHERE
user = "" AND
pass = "Abrakadabra" OR ""=""
Die Bedingung ""="" ist immer wahr. Diese Query liefert deshalb sämtliche Datensätze der Tabelle zurück. Die nachfolgende if-Klausel ist plötzlich wahr und so wird der Benutzer als korrekt angemeldet identifiziert, obwohl er weder über Benutzernamen noch Passwort verfügt.
Lösungsmöglichkeiten
Escapen
Jede Datenbankschnittstelle bietet eine Funktion, mit der sich gefährliche Zeichen escapen und damit entschärfen lassen:
PHP Code:
$user = isset($_POST['user']) ? $_POST['user'] : '';
$pass = isset($_POST['pass']) ? $_POST['pass'] : '';
// so geht das bei der "guten" alten mysql-Extension:
if ($user && $pass)
{
$sql = 'SELECT * FROM benutzer WHERE
user = "' . mysql_real_escape_string($user) . '" AND
pass = "' . mysql_real_escape_string($pass) . '"';
$result = mysql_query($sql);
}
// so geht das bei der neuen mysqli-Extension:
if ($user && $pass)
{
$sql = 'SELECT * FROM benutzer WHERE
user = "' . $mysqli->escape_string($user) . '" AND
pass = "' . $mysqli->escape_string($pass) . '"';
$result = $mysqli->query($sql);
}
// und so geht das bei PDO (nicht nur bei MySQL):
if ($user && $pass)
{
$sql = 'SELECT * FROM benutzer WHERE
user = "' . $pdo->quote($user) . '" AND
pass = "' . $pdo->quote($pass) . '"';
$result = $pdo->query($sql);
};
Warnung:
Magic Quotes sind kein Ersatz für datenbankspezifische escape-Funktionen. Diese Zeichen werden bei Magic Quotes escaped:
und diese bei mysql_real_escape_string:
Code:
\x00, \x1a, \n, \r, \, ' und "
Magic Quotes sind also unzureichend.
Prepared Statements
Bei Prepared Statements zerlegt man die Queries in zwei Teile: SQL und Daten. Im ersten Schritt wird nur das SQL an die Datenbank geschickt und dort vorbereitet, d.h. geparst und optimiert. Im zweiten Schritt werden die Daten dem SQL hinterhergeschickt. Da so SQL und Daten niemals im selben String zusammentreffen, entstehen auch keine Probleme durch sperrige Zeichen. Das Escapen kann man sich hier also sparen:
PHP Code:
$user = isset($_POST['user']) ? $_POST['user'] : '';
$pass = isset($_POST['pass']) ? $_POST['pass'] : '';
// bei der mysql-Extension gibt's keine prepared Statements!
// Prepared Statements bei mysqli:
if ($user && $pass)
{
// Abschicken des SQLs:
$stmt = $mysqli->prepare(
'SELECT * FROM benutzer WHERE user = ? AND pass = ?'
);
// Welche Variablen sollen beim execute() benutzt werden?
$stmt->bind_param('ss', $user, $pass);
// Abschicken der Daten:
$stmt->execute();
}
// Prepared Statements bei PDO:
if ($user && $pass)
{
// Abschicken des SQLs:
$stmt = $dbh->prepare(
'SELECT * FROM benutzer WHERE user = :user AND pass = :pass'
);
// Welche Variablen sollen beim execute() benutzt werden?
$stmt->bindParam(':user', $user);
$stmt->bindParam(':pass', $pass);
// Abschicken der Daten:
$stmt->execute();
}
Tipp:
Bei Queries, die mehrmals mit verschiedenen Datensätzen durchgeführt werden, bringen prepared Statements außerdem erhebliche Geschwindigkeitsvorteile, da das Statement nur ein einziges Mal vorbereitet werden muß und anschließend beliebig oft zur Verfügung steht.
|