SQL-Injection (dt. SQL-Einschleusung) ist das Ausnutzen einer Sicherheitslücke in Zusammenhang mit SQL-Datenbanken. Die Sicherheitslücke entsteht durch einen Programmierfehler in einem Programm, das auf die Datenbank zugreift. Durch diesen Programmierfehler kann ein Angreifer Datenbankbefehle einschleusen und abhängig vom Einzelfall weitere Daten aus der Datenbank auslesen, Daten unberechtigt ändern oder löschen oder sogar die Kontrolle über den kompletten Datenbankserver übernehmen.

Voraussetzungen Ein erfolgreiches Ausnutzen einer SQL-Injection erfordert: eine SQL-Datenbank, die angegriffen wird, zum Beispiel MySQL, Microsoft SQL Server, Db2 ein Anwendungsprogramm, in das Angreifer Daten eingeben können, zum Beispiel eine Loginmaske, eine Produktsuche in einem Onlineshop oder ein Kontaktformular im Web dass das Anwendungsprogramm diese Daten an die Datenbank weiterreicht einen Programmierfehler in dem Anwendungsprogramm, beim Weiterreichen der Daten

Der Programmierfehler besteht darin, dass das Anwendungsprogramm die Eingabedaten nicht als reine Daten an die Datenbank weiterreicht, sondern aus diesen Daten die Datenbankabfrage erzeugt. Dadurch kann ein Angreifer versuchen, Teile der Datenbankabfrage gezielt zu steuern.

In der Datenbankabfragesprache SQL haben einige Zeichen eine besondere Bedeutung, insbesondere: der Apostroph, um Textdaten zu umschließen das Anführungszeichen, um Textdaten zu umschließen (nur einige Datenbanken) der umgekehrte Schrägstrich (Backslash), um das nachfolgende Zeichen nicht als besonderes Zeichen zu interpretieren das Semikolon, um einen Datenbankbefehl von dem nächsten Datenbankbefehl zu trennen ein doppelter Bindestrich, um den Rest der Zeile zu ignorieren (Kommentar)

Dokumentenorientierte NoSQL Datenbanken können ebenfalls von diesem Sicherheitsproblem betroffen sein:

die geschweiften und eckigen Klammern, welche ein JSON Objekt bzw. Array umschließen

Diese Zeichen verwendet ein Angreifer gezielt, um den Angriff durchzuführen.

Beispiel Ein Beispiel für eine SQL-Abfrage, die in einem Redaktionssystem nach Artikeln aus einer bestimmten Kategorie sucht, ist:

SELECT id, titel, artikeltext FROM artikel WHERE kategorie = 'gesuchte Kategorie';

Diese Abfrage hat folgende Bestandteile: Die großgeschriebenen Wörter sind Schlüsselwörter in SQL und haben eine feste Bedeutung. Die kleingeschriebenen Wörter sind die Namen von Tabellen, Zeilen oder Spalten in der Datenbank. Text innerhalb von einfachen Anführungszeichen ist feststehender Text.

Der Text „gesuchte Kategorie“ stellt hier nur einen Platzhalter dar, an dieser Stelle der Datenbankabfrage muss die jeweils gesuchte Kategorie eingetragen werden. Solange die gesuchte Kategorie keins der oben erwähnten Sonderzeichen enthält, könnte sie direkt in die Beispielabfrage eingesetzt werden.

Wenn die gewünschte Kategorie jedoch zum Beispiel Rock ’n’ Roll ist und damit einen Apostroph enthält, führt ein naives Ersetzen des Platzhalters zu dieser SQL-Abfrage:

SELECT id, titel, artikeltext FROM artikel WHERE kategorie = 'Rock 'n' Roll';

Auffällig ist hier die Doppelbedeutung des Apostroph: einerseits als Begrenzer, andererseits als wörtlich gemeintes Zeichen. Diese Doppelbedeutung ist für Menschen ersichtlich, aus Sicht der Datenbank ist diese Anfrage jedoch nicht sinnvoll interpretierbar, da die Abfrage als kategorie = 'Rock ' interpretiert wird, gefolgt von einem Wort n und dem weiteren Text ' Roll' in Apostrophen. Mit dem Wort n kann die Datenbank nichts anfangen und wird für diese Abfrage daher eine Fehlermeldung zurückgeben.

Eine SQL-Injection besteht nun darin, dass der Angreifer durch gezielte Wahl der Eingabedaten die Datenbankabfrage so verändert, dass sie zwar noch eine gültige Struktur hat, aber eine andere Bedeutung. So führt zum Beispiel der scheinbar unsinnige Suchbegriff asdfasdf' OR id < 100 OR kategorie = 'sdfgsdfg zu dieser Datenbankabfrage:

SELECT id, titel, artikeltext FROM artikel WHERE kategorie = 'asdfasdf' OR id < 100 OR kategorie = 'sdfgsdfg';

Diese Abfrage ist aus Sicht der Datenbank völlig korrekt, sie enthält 3 einzelne Bedingungen, von denen mindestens eine wahr sein muss. Die erste und dritte Bedingung haben in diesem Beispiel nur die Aufgabe, dass die SQL-Abfrage weiterhin wohlgeformt ist, und es ist unwahrscheinlich, dass es in der Datenbank tatsächlich Kategorien dieses Namens gibt. Daher bleibt als einzig relevante Bedingung id < 100 übrig. Aus der ursprünglichen SQL-Abfrage, die nur Artikel aus einer bestimmten Kategorie liefert, ist durch geschickte Wahl des Suchbegriffs eine strukturell andere SQL-Abfrage geworden, die nun die Artikel anhand ihrer ID sucht, unabhängig von der Kategorie.

Durch diese geänderte SQL-Abfrage kann ein Angreifer möglicherweise Daten aus der Datenbank sehen, die nicht für ihn bestimmt sind. Durch andere gezielte Suchbegriffe kann der Angreifer möglicherweise Daten in der Datenbank ändern oder löschen, je nach Einzelfall.

Der Programmierfehler besteht bei einer SQL-Injection darin, den Suchbegriff 1:1 in die Datenbankabfrage zu übernehmen, ohne die besondere Bedeutung des Apostroph und ähnlicher Zeichen zu berücksichtigen.

Vorgang

Veränderung von Daten

Auf einem Webserver befindet sich das Script find.cgi zum Anzeigen von Artikeln. Das Script akzeptiert den Parameter „ID“, welcher später Bestandteil der SQL-Abfrage wird. Folgende Tabelle soll dies illustrieren:

 Erwarteter AufrufAufrufhttp://webserver/cgi-bin/find.cgi?ID=42Erzeugtes SQLSELECT author, subject, text FROM artikel WHERE ID=42;  SQL-InjectionAufrufhttp://webserver/cgi-bin/find.cgi?ID=42;UPDATE+USER+SET+TYPE="admin"+WHERE+ID=23Erzeugtes SQLSELECT author, subject, text FROM artikel WHERE ID=42;UPDATE USER SET TYPE="admin" WHERE ID=23;

Dem Programm wird also ein zweiter SQL-Befehl untergeschoben, der die Benutzertabelle modifiziert.

Datenbank-Server verändern

Auf einem Webserver findet sich das Script search.aspx zum Suchen nach Webseiten. Das Script akzeptiert den Parameter „keyword“, dessen Wert später Bestandteil der SQL-Abfrage wird. Folgende Tabelle soll dies illustrieren:

 Erwarteter AufrufAufrufhttp://webserver/search.aspx?keyword=sqlErzeugtes SQLSELECT url, title FROM myindex WHERE keyword LIKE '%sql%'  SQL-InjectionAufrufhttp://webserver/search.aspx?keyword=sql'+;GO+EXEC+cmdshell('shutdown+/s')+--Erzeugtes SQLSELECT url, title FROM myindex WHERE keyword LIKE '%sql' ;GO EXEC cmdshell('shutdown /s') --%'

Hier wird der eigentlichen Abfrage ein weiterer Befehl angehängt. Die zwei Bindestriche -- kommentieren das Hochkomma als Überbleibsel der eigentlichen Anfrage aus, womit es ignoriert wird. Die nun generierte Abfrage ermöglicht das Ausführen eines Windows-Prozesses, hier illustriert durch das erzwungene Herunterfahren des Servers (sofern der Prozess Administratorrechte hat). Aber auch Daten oder Ähnliches lassen sich dadurch erzeugen (am Beispiel Microsoft SQL Server).

Ausspähen von Daten

Auf manchen SQL-Implementationen ist die UNION-Klausel verfügbar. Diese erlaubt es, mehrere SELECTs gleichzeitig abzusetzen, die dann eine gemeinsame Ergebnismenge zurückliefern. Durch eine geschickt untergeschobene UNION-Klausel können beliebige Tabellen und Systemvariablen ausgespäht werden.

 Erwarteter AufrufAufrufhttp://webserver/cgi-bin/find.cgi?ID=42Erzeugtes SQLSELECT author, subject, text FROM artikel WHERE ID=42;  SQL-InjectionAufrufhttp://webserver/cgi-bin/find.cgi?ID=42+UNION+SELECT+login,+password,+'x'+FROM+userErzeugtes SQLSELECT author, subject, text FROM artikel WHERE ID=42 UNION SELECT login, password, 'x' FROM user;

Das x beim UNION SELECT ist nötig, weil alle mit UNION verknüpften SELECTs die gleiche Anzahl von Spalten haben müssen. Der Angreifer kann die Anzahl der Spalten herausfinden, indem er ID=42 order by x-- anhängt. Wenn die Seite beispielsweise bei x = 8 normal lädt, aber bei x = 9 eine Fehlermeldung oder andere Seite zeigt, dann ist die Spaltenanzahl 8.

Ist der Datenbankserver fehlerhaft konfiguriert und hat beispielsweise ein aktuell mit der Datenbank verbundener Benutzer, über den die SQL-Injection abgesetzt werden soll, Zugriff auf Systemdatenbanken, so kann der Angreifer über eine einfache SQL-Syntax wie Systemdatenbank.SystemtabelleMitTabellenAuflistung auf die Systemtabellen zugreifen und sämtliche Tabellen einer bestimmten Datenbank auslesen. Dadurch kann er wichtige Informationen erhalten, um weitere Angriffe durchzuführen und tiefer in das System einzudringen.

Bei MySQL-Datenbanksystemen werden diese Systeminformationen in der Datenbank information_schemaDie Datenbank INFORMATION_SCHEMA. MySQL 5.1 Referenzhandbuch, Kapitel 20 verwaltet. Das nachfolgende Beispiel zeigt, wie bei einer Abfrage mit 3 Ergebnisspalten die Struktur sämtlicher zugreifbarer Datenbanken in Erfahrung gebracht werden kann.  Erwarteter AufrufAufrufhttp://webserver/cgi-bin/find.cgi?ID=42Erzeugtes SQLSELECT author, subject, text FROM artikel WHERE ID=42;  SQL-InjectionAufrufhttp://webserver/cgi-bin/find.cgi?ID=42+UNION+SELECT+'Datenbank',+'Tabelle',+'Spalte'+UNION+SELECT+table_schema,+table_name,+column_name+FROM+information_schema.columns+WHERE+NOT+table_schema='information_schema';#%20Erzeugtes SQLSELECT author, subject, text FROM artikel WHERE ID=42 UNION SELECT 'Datenbank', 'Tabelle', 'Spalte' UNION SELECT table_schema, table_name, column_name FROM information_schema.columns WHERE NOT table_schema='information_schema';# ;

Einige Datenbanksysteme bieten weiterhin die Möglichkeit, Dateien über eine Anfrage zurückzugeben. Hierüber können in Kombination mit oben genannten Techniken und soweit der Pfad bekannt ist, beliebige Dateien, auf die der Datenbankprozess Zugriff hat, ausgelesen werden.

Ein ähnlicher Aufruf in einer MongoDB NoSQL Datenbank könnte so aussehen.  Erwarteter AufrufAufrufhttp://webserver/cgi-bin/find.cgi?ID=42Erzeugtes NoSQLdb.users.find({ userID: 42 });  NoSQL-InjectionAufrufhttp://webserver/cgi-bin/find.cgi?ID['$ne']=Erzeugtes NoSQLdb.users.find({ userID: { '$ne': undefined } });

$ne ist der Operator für „ungleich“, die Abfrage gibt die Benutzer zurück, deren Benutzername nicht leer ist, d. h. alle Benutzer.

Einschleusen von beliebigem Code

Eine weniger bekannte Variante stellt gleichzeitig die potenziell gefährlichste dar. Wenn der Datenbankserver die Kommandos SELECT … INTO OUTFILE beziehungsweise SELECT … INTO DUMPFILE unterstützt, können diese Kommandos dazu benutzt werden, Dateien auf dem Dateisystem des Datenbankserver abzulegen. Theoretisch ist es dadurch möglich, falls das Bibliotheksverzeichnis des Betriebssystems oder des Datenbankservers für denselben beschreibbar ist (wenn dieser zum Beispiel als root läuft), einen beliebigen Code auf dem System auszuführen.

Zeitbasierte Angriffe

Wenn der Datenbankserver Benchmark-Funktionen unterstützt, kann der Angreifer diese dazu nutzen, um Informationen über die Datenbankstruktur in Erfahrung zu bringen. In Verbindung mit dem if-Konstrukt sind der Kreativität des Angreifers kaum Grenzen gesetzt. Das Kopieren von Datenbankinhalten dauert allerdings wesentlich länger.

Das folgende Beispiel benötigt auf einem MySQL-Datenbankserver mehrere Sekunden, falls der gegenwärtige User root ist:

SELECT IF( USER() LIKE 'root@%', BENCHMARK(100000,SHA1('test')), 'false');

Verwandt dazu sind Boolesche Angriffe, die durch Boolesche Abfragen versuchen, Datenbankinhalte zu extrahieren.

Erlangen von Administratorrechten Bei bestimmten Datenbankservern, wie dem Microsoft SQL Server bis zur Version 2000, wurden Stored Procedures wie Xp_cmdshell automatisch angeboten, die unter anderem dazu missbraucht werden können, Kommandos mit den Rechten des SQL-Serverprogramms auszuführen. Neuere Versionen des Microsoft SQL Server haben diese Funktion standardmäßig deaktiviert. Diese Möglichkeit konnte dazu benutzt werden, um zum Beispiel eine Shell auf dem angegriffenen Rechner zu starten.

Verwundbarkeiten innerhalb des Datenbankservers