PHP > SQL-Injection > Best Practice

Zur Beginn erst noch eine kleine Einführung in SQL-Injections.

Was ist eine SQL-Injection?
Wenn Jemand versucht eine Benutzereingabe so zu manipulieren, dass der generierte SQL-Befehl etwas anderes tut, als er soll. Nehmen wir mal folgendes Szenario an: Wir schicken eine URL an unseren WordPress-Blog, die einen zusätzlichen SQL-Befehl ausführt – nämlich die Tabelle xyz löschen.

http://web-union.de/?id=1; DROP TABLE xyz; --

Das generierte SQL-Query würde dann so aussehen:

$sql = "SELECT * FROM posts WHERE id = '{$_GET['id']}'";
SELECT * FROM posts WHERE id = '1'; DROP TABLE xyz; --';

Mit den letzten beiden Strichen wird das schließenden Anführungszeichen und das Semikolon am Ende der SQL-Query einfach auskommentiert.

Und wie lassen sich SQL-Injections effektiv verhindern?
Dazu gibt es einige Methoden, die ich unter anderem auch für Yumee verwende.

1. Benutzereingaben escapen
Das ist relativ schnell gemacht und sollte im Normalfall genug Schutz gegen Angreifer bieten. Ein blinder Angriff wird somit eigentlich ausgeschlossen.

$_GET = array_map('mysql_real_escape_string', $_GET);
$_POST = array_map('mysql_real_escape_string', $_POST);
$_COOKIE = array_map('mysql_real_escape_string', $_COOKIE);

Wer möchte kann so zusätzlich noch $_REQUEST und $_FILES escapen.

Damit diese Methode wirklich sicher funktioniert, muss bei der Verwendung noch einiges beachtet werden:

  • Die Benutzereingaben innerhalb der SQL-Query sollten immer mittels Hochkommata umschlossen werden. Wenn dies nicht möglich ist, unbedingt den Inhalt vorher prüfen (z.B. bei Feldernamen).
  • Auf keinen Fall die Eingaben mit einer anderen Funktion escapen. addslashes() und magic_quotes_gpc sind nicht sicher können ausgetrickst werden.

2. Value Binding mit PDO
Die sicherste Lösung ist wahrscheinlich das Value-Binding. Dabei werden die Werte erstmal durch Platzhalter vorbelegt und später von der PDO-Erweiterung aus PHP eigenständig ersetzt und vorher natürlich escaped. Hier mal ein kleines Beispiel:

$pdo = new PDO('mysql:dbname=DatenbankName;host=localhost', 'benutzer', 'ultra_sicheres_passwort');
$stmt = $pdo->prepare('SELECT * FROM blog_posts WHERE post_title LIKE ?');
$stmt->bindValue(1, $_POST['suche'], PDO::PARAM_STR);
if($stmt->execute())
{
	$result = $stmt->fetchAll();
}

Hier müssen keine Anführungszeichen oder Hochkommata verwendet werden. Die werden später von PDO automatisch – abhängig vom angegebenen Datentyp – gesetzt. Die MySQLi-Extension besitzt nebenbei bemerkt eine ähnliche Funktion.
Diese Lösung bedeutet zwar etwas mehr Code, dafür ist sie aber sauberer und bedeutend sicherer.

3. PHP IDS
Ich hatte bereits vor einiger Zeit über PHP IDS berichtet. Dabei handelt es sich um eine Bibliothek, die vor die eigentliche Anwendung geschaltet wird und vor dem Start sämtliche Benutzereingaben überwacht. Dabei werden nicht nur SQL-Injections erkannt, sondern auch XSS und andere schädliche Eingaben. Eventuelle Angriffe können so geloggt werden, oder auch direkt per Mail an einen Administrator verschickt werden.

Weitere Tipps und Best Practice:

  • Wenn ihr sicher seid, dass die Eingabe eine Zahl sein muss, reicht es auch die Variable in eine Zahl umzuwandeln (z.B. mit dem (int)-Cast). Das ist sicherer als jedes Escapen.
  • Schreibt sauberes SQL und umschließt am besten alles in Hochkommata was möglich ist. Nur so kann das Escapen wirklich funktionieren.
  • Verlasst euch nicht auf die PHP-internen Funktionen magic_quotes_runtime und magic_quotes_gpc. Diese sind seit PHP 5.3 sowieso veraltet und werden in PHP 6 mangels Sicherheit komplett entfernt.
  • Wer wirklich sicher gehen will, überprüft immer alle Eingaben vom Benutzer, ob diese überhaupt einen gültigen Wert haben. Unbedingt auch Tabellen- und Spaltennamen beachten, eben alles was vom Benutzer zur Datenbank geleitet wird.

RSA Verschlüsselung unter PHP

Vor kurzem habe ich einige Präsentationen über Sicherheit in PHP-Anwendungen gesehen. Ein Thema dabei war, dass die gute alte MD5-Verschlüsselung bei Passwörtern nicht mehr ganz so sicher ist, weil es inzwischen einige einfache Methoden zur „Entschlüsselung“ gibt (z.B. mit Rainbow Tables oder Hash-Datenbanken). Ein erster Lösungsansatz zur Verbesserung der Passwortverschlüsselung ist häufig ein „Salt“ an den MD5-Hash anzuhängen, um es dem Angreifer zumindest zu erschweren. Aus meiner Sicht ist das allerdings mehr ein Workaround als eine wirklich sichere Lösung, daher habe ich nach einer Alternative zu MD5 gesucht und bin bei der RSA-Verschlüsselung hängengeblieben. Da es etwas kniffelig ist, die RSA-Verschlüsselung in einer PHP-Anwendung zu implementieren, gibt es von mir hier ein kleines HowTo. Ihr könnt die Verschlüsselung natürlich nicht nur für Passwörter verwenden, sondern für alle möglichen Daten.

» Weiterlesen

SQL Injections Ade: PHP IDS

Nachdem mein Browsergame Yumee letzte Woche wahrscheinlich Opfer einer SQL-Injection geworden ist (fragt mich nicht wie das ging), habe ich kurzer Hand entschieden, die Library PHP IDS einzusetzen.

Kurz zu Einleitung: IDS kommen ursprünglich aus der Netzwerktechnik und überwachen dort Netzwerke auf mögliche Angriffe. Und genau das tut auch die PHP IDS. Wir lassen alle eingehenden Variablen (GET, POST und COOKIE), bevor die Anwendungen damit arbeitet, einmal überprüfen. Wenn die IDS nicht anschlägt, geht der Aufruf durch, ansonsten beenden wir Anwendung einfach.

PHP IDS hat neben seiner Hauptfunktion allerdings noch weitere Features. So lässt sich beispielsweise der HTML Pruifier anbinden, der eingehende Strings nach bösen HTML-Code durchsucht (z.B. <script> oder onclick). Außerdem gibt es verschiedene Logging-Möglichkeiten + automatische E-Mail-Benachrichtigung.
Einziger Nachteil: Es geht ein wenig Performance verloren, wobei das kaum zu merken ist.