SQL-Injection

Sicherheitslücke
Dies ist eine alte Version dieser Seite, zuletzt bearbeitet am 22. Dezember 2004 um 11:04 Uhr durch 145.253.188.74 (Diskussion) (Java). Sie kann sich erheblich von der aktuellen Version unterscheiden.

SQL Injection (zu deutsch SQL-Injektion) bezeichnet das Ausnutzen einer Computersicherheits-Lücke. Der Angreifer versucht SQL-Abfragen zu manipulieren. Hierzu werden über die Applikation, die den Zugriff auf die Datenbank bereitstellt, SQL Statements eingefügt.

Oft zu finden sind SQL Injection-Lücken in CGI Scripten, aber auch Programme, die andere Daten - etwa Webseiteninhalte oder E-Mails in SQL-Datenbanken - eintragen, sind anfällig. Es wird versucht, weitere SQL-Anforderungen einzuschleusen oder die Abfragen so zu manipulieren, dass man zusätzliche Daten erhält. Manche Datenbanksysteme bieten auch die Möglichkeit, Zugriff auf eine Shell zu erhalten, womit der ganze Server kompromitierbar wird.

SQL Injection Bugs treten auf, wenn eine Applikation SQL-Abfragen an den Server weiterreicht, ohne benutzerveränderbare Parameter mit Fluchtzeichen zu versehen. So muss in Zeichenfolgen das '-Zeichen und in einigen Datenbanken zusätzlich der \ durch voranstellen eines umgekehrten Schrägstriches maskiert werden: \' bzw. \\. Außerdem muss sichergestellt werden, dass Zahlen nur aus Ziffern und dem Dezimaltrennzeichen bestehen.

Stored Procedures sind in diesem Zusammenhang sicherer. In einem Programm übergibt man die Benutzereingaben den Stored Procedures, erst hier wird eine SQL Query erzeugt und ausgeführt. Die Stored Procedures sind deshalb besonders sicher, da sie keine weiteren SQL-Befehle in Benutzereingaben akzeptieren.

Vorgang

Veränderung von Daten

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

  Keine SQL-Injektion
Aufruf http://webserver/cgi-bin/find.cgi?ID=42
Erzeugtes SQL SELECT author, subjekt, text FROM artikel WHERE artikel ID=42
  SQL-Injektion
Aufruf http://webserver/cgi-bin/find.cgi?ID=42;UPDATE%20USER%20SET%20TYPE="admin"%20WHERE%20ID=23
Erzeugtes SQL SELECT author, subjekt, text FROM artikel WHERE ID=42; UPDATE USER SET TYPE="admin" WHERE ID=23

Wie man erkennen kann, wird dem Programm 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, welcher später Bestandteil des SQL-Befehls wird. Folgende Tabelle soll dies illustrieren:

  Keine SQL-Injektion
Aufruf http://webserver/search.aspx?keyword=sql
Erzeugtes SQL SELECT url, title FROM myindex WHERE keyword LIKE '%sql'
  SQL-Injektion
Aufruf http://webserver/search.aspx?keyword=sql' GO EXEC cmdshell('format C') --
Erzeugtes SQL SELECT url, title FROM myindex WHERE keyword LIKE '%sql' GO EXEC cmdshell('format C') --'

Hier wird der eigentlichen Abfrage ein weiterer Befehl angehängt. Die zwei Bindestriche (--) kommentieren das Hochkomme als Überbleibsel der eigentlichen Anfrage aus. Der Befehl ermöglicht das Formatieren der Festplatte, aber auch Downloads 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 Ergebnis-Menge zurückliefern. Durch eine geschickt untergeschobene UNION Klausel kann man beliebige Tabellen und Systemvariablen auslesen.

  Keine SQL-Injektion
Aufruf http://webserver/cgi-bin/find.cgi?ID=42
Erzeugtes SQL SELECT text FROM WHERE artikel ID=42
  SQL-Injektion
Aufruf http://webserver/cgi-bin/finduser.cgi?ID=42%20UNION%20SELECT%20login,%20password,%201%20FROM%20user
Erzeugtes SQL SELECT author, subjekt, text FROM artikel WHERE ID=42 UNION SELECT login, password, 1 FROM user

Die 1 beim UNION SELECT ist nötig, weil alle mit UNION verknüpften SELECTs die gleiche Anzahl von Spalten haben müssen. Der Angreifer muss also wissen wieviele Spalten die ursprüngliche Abfrage hat.

Einschleusen von beliebigen Code

Eine weniger bekannte Variante stellt auch gleichzeitig die potentiell gefährlichste dar. Wenn der Datenbankserver die Kommandos SELECT ... INTO OUTFILE bzw. 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 z.B. als root läuft), 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 folgende Beispiel pausiert auf einem MySQL Datenbankserver mehrere Sekunden falls der gegenwärtige User root ist:

SELECT if( user() like 'root@%', benchmark(100000,sha1('test')), 'false');

Erlangung von Adminstratorrechten

Bei bestimmten Datenbankservern, wie dem Microsoft SQL Server werden Stored Pocedures mitgeliefert, die unter anderen dazu missbraucht werden können, um einen neuen Benutzer auf dem angegriffenen System anzulegen.

Diese Möglichkeit kann dazu benutzt werden, um zum Beispiel eine Shell auf dem angegriffenen Rechner zu starten.

Gegenmaßnahmen

Es ist nicht schwer bestehende Programme so umzubauen, dass SQL-Injektionen nicht mehr möglich sind. Das hauptsächliche Problem der meisten Programmierer ist fehlendes Wissen über diese Art von Angriffen. Nachfolgend einige Beispiele um die Angriffe abzuwehren.

Außerdem sollten nicht benötigten Stored Procedures aus dem Datenbanksystem entfernt werden. Beim Microsoft SQL Server sollte auch geprüft werden, ob der Einsatz unter einem Account mit eingeschränkten Zugriffsrechten möglich ist, was in der Regel der Fall sein dürfte.

Microsoft .NET Framework

Im .NET Framework gibt es einfache Objekte, mit denen man solche Probleme umgeht.

Anstatt

SqlCommand cmd = new SqlCommand("SELECT spalte1 FROM tabelle WHERE spalte2 = '" + spalte2Wert + "';");

sollte folgendes verwendet werden

string spalte2Wert = "Mein Wert";
SqlCommand cmd = new SqlCommand("SELECT spalte1 FROM tabelle WHERE spalte2 = @spalte2Wert;");
cmd.Parameters.Add("@spalte2Wert", spalte2Wert);

Java

SQL Injection kann leicht durch bereits vorhandene Funktion verhindert werden. In Java wird zu diesem Zweck die PreparedStatement Klasse verwendet.

Anstatt

Statement stmt = con.createStatement();
ResultSet rset = stmt.executeQuery("SELECT spalte1 FROM tabelle WHERE spalte2 = '" + spalte2Wert + "';");

sollte folgendes verwendet werden

PreparedStatement pstmt = con.prepareStatement("SELECT spalte1 FROM tabelle WHERE spalte2 = ?");
pstmt.setString(1, spalte2Wert);
ResultSet rset = stmt.executeQuery();

PHP

In PHP wird zu diesem Zweck die Funktion mysql_real_escape_string() verwendet.

Anstatt

$abfrage = "SELECT spalte1 FROM tabelle WHERE spalte2 = '".$_POST['spalte2Wert']."'";
$query = mysql_query($abfrage) or die("Datenbankabfrage ist fehlgeschlagen!");

sollte folgendes verwendet werden

$abfrage = "SELECT spalte1 FROM tabelle WHERE spalte2 = '".mysql_real_escape_string($_POST['spalte2Wert'])."'";
$query = mysql_query($abfrage) or die("Datenbankabfrage ist fehlgeschlagen!");

Perl

Das datenbankunabhängige Datenbankmodul DBI unterstützt eine ähnliche "prepare" Syntax die auch im Java Beispiel zu sehen ist.

$statementhandle = $databasehandle->prepare("SELECT spalte1 FROM tabelle WHERE spalte2 = ?");
$returnvalue = $statementhandle->execute( $spalte2Wert );

MS-SQL

Über parametrisiertes Kommando kann die Datenbank vor SQL-Code-Injection geschützt werden:

   SELECT COUNT(*) FROM Users WHERE UserName=? AND UserPassword=?

ORACLE (dynamic PL/SQL)

Siehe auch