Heute möchte ich das Thema „Sicherheit in SQL Queries“ ansprechen. Immer wieder hört man von „SQL-Injection“ und den damit verbundenen Schäden (von Datenklau, zerstörten Datenbanken usw.). Es ist natürlich ein heikles Thema – Doch es gibt auch vieles was man dagegen tun kann.

Zuerst – Was genau ist eine „SQL-Injection“?
Man spricht von SQL-Injection wenn eigener SQL Code (durch eine Sicherheitslücke) in eine Query einer Web-Applikation eingeschleust werden kann. Ich werde das ganze an einem kleinen Beispiel erklären…

Wir gehen von einem Blog aus, dessen Einträge über eine übergebene ID in der URL aufgerufen werden – Eine Beispielhafte URL wäre:

http://web-developer-blog.com/?post_id=5

Wir möchten anhand der übergebenen ID einen Eintrag aus unserer MySQL Tabelle „posting“ selektieren. Hier sehen wir einen Ausschnitt des Quellcodes, der den entsprechenden SQL Code bereitstellt:

$sql = "SELECT * FROM posting WHERE id = " . $_GET['post_id'];

(Achtung, dieser Code ist extrem unsicher und sollte in dieser Form niemals in einer Web-Applikation zum Einsatz kommen!)

Der resultierende SQL Code würde folgendermaßen aussehen:

SELECT * FROM posting WHERE id = 5

Eine SQL-Injection lässt sich in diesem Fall mit dem Aufruf folgender URL provozieren:

http://web-developer-blog.com/?post_id=5;DROP+DATABASE+blog

Da wir die übergebene ID nicht filtern oder validieren würde folgende SQL Query zustande kommen:

SELECT * FROM posting WHERE id = 5;DROP DATABASE blog

Und schon wird unsere komplette Datenbank („blog“) gelöscht.

Natürlich ist diese SQL-Injection verhältnismäßig „plump“, doch ich denke ihr versteht in welche Richtung das ganze geht.

Falls ihr für die Entwicklung ein Framework benutzt (das über eine Datenbankabstraktion verfügt) sollte eigentlich für ein gewisses Maß an Sicherheit gesorgt sein – Da kommt es aber natürlich auf das Framework eurer Wahl an. Einige bringen sogenannte „Query-builder“ mit: Eine Klasse die beim erstellen von SQL Queries behilflich ist.

Das folgende Beispiel kommt aus dem Kohana Framework:

$query = DB::select()->from('posting')->where('id', '=', $_GET['post_id']);

Zusätzlich zum Objektorientierten arbeiten mit der Datenbank bringen solche Query-builder in der Regel einige Sicherheitsfeatures mit – Soll heißen sie sorgen (soweit möglich) dafür das kein Schadcode im SQL Query landet.

Doch was, wenn man kein Framework benutzt? Was sind die Tipps für den Web-Entwickler der seine Queries gerne selbst tippt?

Für diejenigen habe ich hier mal die wichtigsten Punkte zusammengestellt:

Sofern es euch möglich ist, solltet ihr eure SQL Queries immer filtern und nur genau das selektieren/speichern was auch erlaubt ist und Sinn macht. Dafür gibt es viele Hilfsmittel und einfache Methoden – Für mein Beispiel von oben gibt es eine ganz einfache und performante Lösung:

$sql = "SELECT * FROM posting WHERE id = " . (int) $_GET['post_id'];

Hier wird der Inhalt der Variable zu einem „Integer“, einer Ganzzahl, umgewandelt. Sollte nun irgend etwas in der Variable stehen das keine Zahl ist, wird es kurzerhand entfernt.
Ein sogenanntes „Type-Casting“ kann also bereits helfen eine SQL-Injection zu verhindern. Doch sobald man nicht mehr mit Zahlen arbeitet wird das ganze etwas schwieriger!

Glücklicherweise bietet uns PHP einige Funktionen zum filtern.
Im großen und ganzen ist das filtern von Strings mit Hilfe von mysql_real_escape_string() ausreichend. Diese Funktion sollte für eingaben von „nicht vertrauenswürdigen Quellen“ benutzt werden – also z.B. Formulare die von euren Web-Applikationen direkt in die Datenbank laufen.

Übrigens, das Wort „Web-Applikation“ kann für eine tatsächliche Applikation (wie z.B. Google Docs) oder eine Webseite stehen.

Anschließend wieder ein Quellcode Beispiel – Dieses mal wollen wir einen Autoren selektieren. Die URL dazu lautet:

http://web-developer-blog.com/?autor=leo

Der Quellcode-Ausschnitt sollte in etwa so aussehen:

$sql = "SELECT * FROM authors WHERE name LIKE '" . mysql_real_escape_string($_GET['autor']) . "'";

Daraus ergibt sich nun das folgende SQL Statement:

SELECT * FROM authors WHERE name LIKE 'leo'

Für unseren SQL-Injection Versuch werden wir die URL wieder etwas manipulieren:

http://web-developer-blog.com/?name=leo;'DROP+DATABASE+blog

Durch die Nutzung von mysql_real_escape_string wird nun folgender Befehl an die Datenbank weitergeleitet:

SELECT * FROM authors WHERE name LIKE 'leo;\'DROP DATABASE blog'".

Durch diese Funktion seid ihr also auf der richtigen Seite! Die SQL-Injection ist fehlgeschlagen da das Hochkomma durch einen Backslash maskiert wurde und das Query nicht manipuliert werden konnte. Dennoch kann ich nur jedem ans Herz legen seine SQL Queries zusätzlich zu filtern – Je nach Anwendungszweck:

  • In einem Datumsfeld sollte z.B. nur ein Datum stehen und kein sonstiger String!
  • In einem Feld für E-Mail-Adressen sollen wirklich NUR E-Mail-Adressen gespeichert werden (Für solche „validierungen“ gibt es demnächst einen Blog-Eintrag)

Und ich möchte noch einige Punkte ergänzen:

  • Die addslashes() Funktion würde für mein Beispiel zwar ein identisches Ergebnis liefern, doch es gibt diverse Feinheiten (speziell auf SQL bezogen) die durch addslashes() nicht abgedeckt werden können.
  • Falls ihr euch denkt „Über SQL-Injection brauche ich mir keine Gedanken machen, ich benutze die ‚magic_quotes‘ von PHP.“ lasst mich euch sagen das die magic_quotes einerseits extrem unsicher sind und ihnen niemals blind vertraut werden sollte! Darüber hinaus ist diese Option seit PHP 5.3 als deprecated (veraltet) markiert und wird voraussichtlich ab PHP 6 nicht mehr existieren und entsprechende Fehler verursachen.
  • Regex Prüfungen sind sicherlich sinnvoll um ungewollte Zeichenketten aus euren SQL Queries zu entfernen, doch ihr solltet auch bedenken das komplexe Regex Suchmuster stark an der Performanz nagen und es (je nach Fall) extrem schwierig bis unmöglich werden kann einen passenden Regex Code zu erstellen. Doch für eine Filterung nach Datum, E-Mail-Adresse und sonstigen einfachen Mustern sind sie vollkommen okay!
  • Vertraut niemandem! Im Internet gibt es genug Publikum die „nur zum Spaß“ solche Sicherheitslücken suchen und ausnutzen – Baut lieber eine Filterung zu viel ein als eine Sicherheitslücke zu riskieren.

Ihr merkt schon, zu diesem Thema lassen sich ganze Bücher schreiben, doch bevor es so weit kommt möchte ich meinen Beitrag hier beenden – Ich hoffe ihr habt nun einen gewissen Einblick in das Thema und könnt mit meinen Tipps etwas anfangen!
Solltet ihr sonstige Vorschläge oder Ideen zum Thema haben, hinterlasst mir bitte ein Kommentar.