SQL-Injection
SQL Injection (zu deutsch deutsch SQL Injektion) bezeichnet das Ausnutzen einer Computersicherheits-Lücke. Der Angreifer versucht SQL Abfragen zu manipulieren. Hierzu wird über die Applikation, die den Zugriff auf die Datenbank bereitstellt, SQL Statments einzufügen.
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 einzuschleußen 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 sollten z.B. die Zeichen ' ; " durch \' \; \" ersetzt werden.
Stored Procedures sind 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 WHERE artikel ID=42 |
SQL-Injektion | |
Aufruf | http://webserver/find.cgi?ID=42;UPDATE%20USER%20SET%20TYPE="admin"%20WHERE%20ID=23 |
Erzeugtes SQL | SELECT authur, 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.
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/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 Betriebsystems 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');
Gegenmassnahmen
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.
Java
SQL Injection kann leicht durch bereits vorhandene Funktion verhindert werden. In Java wird zu diesem Zweck die PreparedStatement Klasse verwendet.
Anstatt
Connection con = (acquire Connection) Statement stmt = con.createStatement(); ResultSet rset = stmt.executeQuery("SELECT spalte1 FROM tabelle WHERE spalte2 = '" + spaltenName + "';");
sollte folgendes verwendet werden
Connection con = (acquire Connection) PreparedStatement pstmt = con.prepareStatement("SELECT spalte1 FROM tablle WHERE spalte2 = ?"); pstmt.setString(1, spaltenName); ResultSet rset = stmt.executeQuery();
PHP
In PHP wird zu diesem Zweck die Funktion mysql_escape_string() verwendet.
Anstatt
$abfrage = "SELECT spalte1 FROM tabelle WHERE spalte2 = '".$_POST['spaltenname']."'"; $query = @mysql_query($abfrage) or die("Datenbankabfrage ist fehlgeschlagen!");
sollte folgendes verwendet werden
$abfrage = "SELECT spalte1 FROM tabelle WHERE spalte2 = '".mysql_escape_string($_POST['spaltenname'])."'"; $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( $spaltenname );