SQL-InjectionSQL-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. VoraussetzungenEin erfolgreiches Ausnutzen einer SQL-Injection erfordert:
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:
Dokumentenorientierte NoSQL Datenbanken können ebenfalls von diesem Sicherheitsproblem betroffen sein: Diese Zeichen verwendet ein Angreifer gezielt, um den Angriff durchzuführen. BeispielEin Beispiel für eine SQL-Abfrage, die in einem Redaktionssystem nach Artikeln aus einer bestimmten Kategorie sucht, ist:
Diese Abfrage hat folgende Bestandteile:
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:
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 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
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 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. VorgangVeränderung von DatenAuf 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:
Dem Programm wird also ein zweiter SQL-Befehl untergeschoben, der die Benutzertabelle modifiziert. Datenbank-Server verändernAuf 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:
Hier wird der eigentlichen Abfrage ein weiterer Befehl angehängt. Die zwei Bindestriche Ausspähen von DatenAuf manchen SQL-Implementationen ist die
Das 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 Bei MySQL-Datenbanksystemen werden diese Systeminformationen in der Datenbank
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.
Einschleusen von beliebigem CodeEine weniger bekannte Variante stellt gleichzeitig die potenziell gefährlichste dar. Wenn der Datenbankserver die Kommandos Zeitbasierte AngriffeWenn 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 AdministratorrechtenBei 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 DatenbankserversManchmal existieren Verwundbarkeiten auch innerhalb der Datenbanksoftware selbst. So erlaubte zum Beispiel die PHP-Funktion mysql_real_escape_string() im MySQL Server einem Angreifer, SQL Injection-basierende Angriffe basierend auf Unicode-Zeichen selbst dann auszuführen, wenn die Benutzereingaben korrekt maskiert wurden. Dieser Fehler wurde in der Version 5.0.22 am 24. Mai 2006 behoben. Blinde SQL-InjectionVon einer blinden SQL-Injection wird gesprochen, wenn ein Server keine deskriptive Fehlermeldung zurückliefert, aus der hervorgeht, ob der übergebene Query erfolgreich ausgeführt wurde oder nicht. Anhand verschiedenster Kleinigkeiten wie etwa leicht unterschiedlicher Fehlermeldungen oder charakteristisch unterschiedlicher Antwortzeiten des Servers kann ein versierter Angreifer häufig dennoch feststellen, ob ein Query erfolgreich war oder einen Fehler zurückmeldet. GegenmaßnahmenUm SQL-Injections zu verhindern, dürfen vom Benutzer eingegebene Daten nicht ohne Weiteres in eine SQL-Anweisung eingebaut werden. Eingabedaten können auf die Eigenschaften erwarteter Werte geprüft werden. So bestehen deutsche Postleitzahlen beispielsweise nur aus Ziffern. Grundidee von Prepared StatementsDer sicherste Weg ist jedoch, die Daten überhaupt vom SQL-Interpreter fernzuhalten.[2] Dabei kann man auf das Kappen der Eingabe verzichten. Die Technik dazu sind gebundene Parameter in Prepared Statements. Dabei werden die Daten als Parameter an einen bereits kompilierten Befehl übergeben. Die Daten werden somit nicht interpretiert und eine SQL-Injection verhindert. Stored Procedures bieten dagegen keinen generellen Schutz vor SQL-Injection, insbesondere dann nicht, wenn der SQL-Code der Funktion nicht bekannt ist. Doch auch auf Seiten des Datenbankservers lassen sich Sicherheitsvorkehrungen treffen. So sollten die Benutzer, mit denen sich eine Webanwendung beim Datenbankserver authentifiziert, nur die Privilegien besitzen, die er tatsächlich benötigt. So können zumindest einige der möglichen Angriffe unwirksam werden. Hat ein Betreiber eines Webservers keine Kontrolle über die Anwendungen, kann durch Einsatz von Web Application Firewalls (WAF) zumindest teilweise verhindert werden, dass SQL-Injection-Schwachstellen ausgenutzt werden können. Unabhängig von der Kontrolle über die Anwendungen kann ein Betreiber eines Webservers durch den gezielten Einsatz einer WAF die Sicherheit zusätzlich erhöhen, da viele WAFs neben abwehrenden auch prophylaktische Maßnahmen anbieten. Es ist nicht schwer, bestehende Programme so umzubauen, dass SQL-Injections 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. Die naive, aber falsche Idee, eine SQL-Abfrage mit beliebigen Eingabedaten anzureichern, sieht in den meisten Programmiersprachen so aus: # gefährlicher Code, enthält die Sicherheitslücke SQL-Injection
sql = "SELECT spalte1 FROM tabelle WHERE spalte2 = '" + eingabedaten + "';"
query = db.query(sql)
Die doppelten Anführungszeichen gehören hier zur Programmiersprache, die einfachen Anführungszeichen gehören zu SQL. Wenn die Eingabedaten selbst auch einfache Anführungszeichen enthalten können, ist eine SQL-Injection möglich. Um das zu verhindern, ist die Idee, die SQL-Anfrage und die Daten zu trennen. Das sql = "SELECT spalte1 FROM tabelle WHERE spalte2 = :eingabedaten;"
query = db.query(sql, eingabedaten=eingabedaten)
PythonIn Python gibt es mehrere Möglichkeiten, mit einer Datenbank zu kommunizieren. Eine davon ist SQLAlchemy. Um hierbei SQL-Injection zu entgehen, sollte man rohe SQL-Befehle vermeiden, sofern diese durch Form- oder URL Anfragen manipuliert werden könnten. Nachfolgend ist ein solches Beispiel zu sehen: db.engine.execute("SELECT * FROM table")
Stattdessen wird dazu geraten die internen Funktionen und Methoden von SQLAlchemy zu nutzen, wie z. B. die Folgenden:[3] session.query(Order).get(order_id)
session.query(Order).filter(Order.status == 'active')
Visual Basic (ADOdb)In Visual Basic gibt es einfache Command-Objekte, mit denen diese Probleme vermieden werden können. Anstatt cn.Execute "SELECT spalte1 FROM tabelle WHERE spalte2 = '" & spalte2Wert & "'"
sollte Folgendes verwendet werden: Dim cmd As ADODB.Command, rs as ADODB.Recordset
With cmd
Set .ActiveConnection = cn
Set .CommandType = adCmdText
.CommandText = "SELECT spalte1 FROM tabelle WHERE spalte2 = ?"
.Parameters.Append .CreateParameter("paramSp2", adVarChar, adParamInput, 25, spalte2Wert) '25 ist die max. länge
Set rs = .Execute
End With
DelphiSeit der BDE kennen die Querys Parameter. Die Syntax ist bei verschiedenen Komponentensammlungen nicht immer identisch aber ähnlich. Anstatt … function TDatabase.GetData(ParameterID: Integer): Integer;
var
Qry: TQuery;
begin
Result := 0;
Qry := TQuery.Create;
try
Qry.SQL.Text := 'SELECT F_DATA FROM T_BLUBB WHERE ID = ' + IntToStr(ParameterID);
Qry.Open;
Result := Qry.FieldByName('F_DATA').AsInteger;
finally
Qry.Free;
end;
end;
… sollte definitiv mit Parametern gearbeitet werden: function TDatabase.GetData(ParameterID: Integer): Integer;
var
Qry: TQuery;
begin
Result := 0;
Qry := TQuery.Create;
try
Qry.SQL.Text := 'SELECT F_DATA FROM T_BLUBB WHERE ID = :PI';
Qry.ParamByName('PI').AsInteger := ParameterID;
Qry.Open;
Result := Qry.FieldByName('F_DATA').AsInteger;
finally
Qry.Free;
end;
end;
Microsoft .NET Framework – C# (ADO.NET)Im .Net-Framework gibt es einfache Objekte, mit denen solche Probleme umgangen werden können. 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.AddWithValue("@spalte2Wert", spalte2Wert);
Java (JDBC)Eine SQL-Injection kann leicht durch eine bereits vorhandene Funktion verhindert werden. In Java wird zu diesem Zweck die PreparedStatement-Klasse verwendet (JDBC-Technologie) und die Daten unsicherer Herkunft werden als getrennte Parameter übergeben. Um die Daten von der SQL-Anweisung zu trennen, wird der Platzhalter „?“ 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 = pstmt.executeQuery();
Der Mehraufwand an Schreibarbeit durch die Verwendung der PreparedStatement-Klasse kann sich außerdem durch einen Performancegewinn auszahlen, wenn das Programm das PreparedStatement-Objekt mehrfach verwendet. PHPIn PHP steht für Datenbankzugriffe die Bibliothek PHP Data Objects zur Verfügung. Beispiel ohne Prepared Statement: $dbh->exec("INSERT INTO REGISTRY (name, value)
VALUES (".$dbh->quote($name,PDO::PARAM_STR).", ".$dbh->quote($value,PDO::PARAM_INT).")");
Beispiel mit Prepared Statement: $stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);
Bis zur PHP-Version 5.3 gab es die Konfigurationsoption „magic_quotes_gpc“. War diese auf „on“ gestellt, wurden von außen kommende Benutzereingaben automatisch maskiert. Manche Skripte nutzen Funktionen wie etwa Nach der PHP-Version 5.3 wurde Grundsätzlich sind auch in puncto Sicherheit immer Prepared Statements vorzuziehen. PerlMit dem datenbankunabhängigen Datenbankmodul DBI, welches normalerweise in Perl verwendet wird: Anstatt $arrayref = $databasehandle->selectall_arrayref("SELECT spalte1 FROM tabelle WHERE spalte2 = $spalte2Wert");
sollte Folgendes verwendet werden: $arrayref = $databasehandle->selectall_arrayref('SELECT spalte1 FROM tabelle WHERE spalte2 = ?',{},$spalte2Wert);
Perls DBI-Modul unterstützt außerdem eine „prepare“-Syntax ähnlich der aus dem Java-Beispiel. $statementhandle = $databasehandle->prepare("SELECT spalte1 FROM tabelle WHERE spalte2 = ?");
$returnvalue = $statementhandle->execute( $spalte2Wert );
Alternativ können über das Datenbankhandle auch Eingabe-Werte sicher maskiert werden. Dabei achtet der DB-Treiber auf die für diese Datenbank typischen Sonderzeichen. Der Programmierer muss keine tiefergehenden Kenntnisse darüber haben. $arrayref = $databasehandle->selectall_arrayref("SELECT spalte1 FROM tabelle WHERE spalte2 = " .
$databasehandle->quote($spalte2Wert) );
Im sogenannten „taint mode“, der skriptweit mit dem Parameter -T aktiviert werden kann, verwendet Perl starke Heuristiken, um nur sichere Zugriffe zu erlauben. Zeichenketten, die vom Benutzer übergebene Parameter enthalten, werden zunächst als „unsicher“ behandelt, bis die Daten explizit validiert wurden, und dürfen vorher nicht in unsicheren Befehlen verwendet werden. ColdFusion Markup LanguageUnter ColdFusion kann das SELECT * FROM courses WHERE Course_ID =
<cfqueryparam value = "#Course_ID#" CFSQLType = "CF_SQL_INTEGER">
MS-SQLÜber parametrisierte Kommandos kann die Datenbank vor SQL-Injections geschützt werden: SELECT COUNT(*) FROM Users WHERE UserName=? AND UserPasswordHash=?
Siehe auchWeblinks
Einzelnachweise und Ressourcen
|
Portal di Ensiklopedia Dunia