Die guten alten Zeiten - da war alles noch einfacher. Oder doch nicht? Ich habe mal an einem größeren Shop-System programmiert - das war im Jahr 2000 - und dabei gelernt, dass man einige Dinge beachten muss: Funktionalität, Usability und ... Security. Ein SQL-Server-System ist ja eine feine Sache, aber es könnte dazu benutzt werden, um "bösen" SQL-Code abzusetzen - wenn man nicht ein paar grundlegende Dinge berücksichtigt.
Die Bezeichnung "SQL-Injection" bedeutet, dass Eingaben (meist durch Webformulare oder präparierte URLs) um syntaktisch korrekte SQL-Befehle erweitert werden, sodass von Außen bösartiger Code eingeschleust werden kann, der Daten ändert oder löscht.
Warum ist SQL Injection jetzt (noch) ein Thema?
Es gab in den letzten Wochen und Monaten einige Mitteilungen, dass Systeme mit IIS und SQL Server durch SQL Injection verwundbar seien.
Diese sind schlichtweg - falsch! Es handelt sich bei diesen Attacken nicht um Schwachstellen von Windows oder IIS oder SQL Server, sondern um Fehler von Software-Lieferanten oder Entwicklern.
Damit Applikationen durch SQL Injection verwundbar sind, müssen die folgenden Voraussetzungen zutreffen:
- Websites und Applikationen, welche Classic ASP (oder auch PHP, od. ASPX) verwenden, die
- SQL Server (oder ähnliche SQL-Datenbanksysteme wie Oracle, MySQL, etc.) verwenden und
- SQL-Befehle ungefiltert zusammensetzen und ausführen (siehe Beispiel unten).
Die Verantwortung für die Softwarelösungen liegt beim Entwickler selbst!
Und hier herrscht leider immer noch viel zu wenig Focus von Software-Entwicklern im Bereich Security, denn sonst wären solche Attacken gegen Webseitensysteme nicht mehr erfolgreich!
Wie funktioniert SQL-Injection?
Auch darüber ist schon viel berichtet worden, hier ein einfaches Beispiel zur Funktionsweise.
Angenommen, es gibt auf einer Webseite eine Login-Seite: Diese verwendet die Texteingaben ungefiltert und baut daraus einen SQL-String zusammen, welcher gegen die Datenbank abgesetzt wird:
SELECT * FROM Users WHERE Username = 'max' and Password = 'geheim'
Das schaut soweit ja fürs Erste funktionell aus - und als Ergebnis werden alle Datensätze mit diesem Filter geliefert - im Idealfall ein Datensatz mit dem User Max, wo sein Kennwort geheim ist. Max wird dann angemeldet und ... zurück zum Start!
Ein findiger, böser Mensch kann nun folgendes versuchen: in der Annahme, dass die User-Abfrage so (oder ähnlich) funktioniert, kann er nun in die Textfelder folgendes schreiben: ' or '1'='1 und dasselbe beim Passwort.
Was kommt beim Zusammensetzen des SQL-Befehls in der Login-Seite raus?
SELECT * FROM Users WHERE Username = '' or '1'='1' and Password = '' or '1'='1'
Die Texteingaben werden in das SQL eingesetzt und liefern somit immer WAHR. Und was liefert diese Abfrage? ALLE Benutzer aus der Users-Tabelle. Das bedeutet zum Beispiel, dass (bei einer Login-Seite) der anonyme Benutzer gleich als erster User in der Tabelle angemeldet wird. Blöd.
Noch blöder: Wenn der Angreifer sich nicht damit begnügt, sondern dahinter noch ein böses SQL absetzt:
1'='1';DELETE FROM Users;
Ganz schlecht. Damit wird beim Ausführen der Abfrage die ganze Users-Tabelle gelöscht. Dumm gelaufen - für den Betreiber der Website.
Optimierung von SQL Injection
Eine weitere Variante - diese ist ganz aktuell aus einem Logfile einer Website entnommen, die mit SQL Injection bearbeitet wurde: Statt od. zusätzlich zur Bedingung wird ein Script eingesetzt, welches aufgerufen wird, wenn die Tabelle ausgelesen und in einer Website angezeigt wird:
<script src="http://www.---boeseseite---.com/b.js"></script>
Und hier drinnen gehts dann mit Javascript weiter... die aufrufende Seite wird zur Umleitung oder (im besten Fall) unbrauchbar.
Suchen Sie mal im Web nach "banner82.com" oder "adw95.com" - dann finden Sie ziemlich viele (auch prominente) Websites, die durch die aktuellen SQL-Injection-Angriffe "ge-hackt" wurden und diese Scripts intus haben oder hatten. Hier ein Beispiel, wie so eine infizierte Seite dann aussehen kann:
Hier sind viele Attacken ausgeführt worden, sodass das Script mehrfach injiziert wurde und teilweise wieder unbrauchbar wird. Noch ein Hinweis zu banner82: Diese Attacke wird nicht ganz im Klartext injiziert, sondern kommt in etwa so daher:
DECLARE @S%20VARCHAR(4000);SET @S=CAST(0x4445434C415245204054205641 ..... 6F7220 AS VARCHAR(4000)); EXEC(@S)
Das SQL wird in einer Variable zusammengebastelt, lädt alle Tabellen und Spaltennamen in einer Schleife und versucht, das Script überall hineinzuschreiben und wird am Schluss mit EXECute ausgeführt.
Eine weitere Steigerung: Mittlerweile ist es nicht mehr so, dass ein Hacker manuell versucht, in die Datenbank einer Website zu einzudringen, sondern die Angriffe werden automatisiert.
Dazu bietet sich zum Beispiel an, ein API von Google od. einer anderen Suchmaschine zu benutzen:
Suche alle Sites, wo Parameter (z.B. http://www.--eineseite--/article?id=xyz) übergeben werden.
Versuche, an den Parameter ein ' anzufügen.
Analysiere die Response der Seite, ob der gesendete Parameter korrektes Encoding besitzt. Wenn nein, dann ist die Website "offen" und ein Kandidat für SQL-Injection-Scripts!
Schutz vor SQL Injection
Die Lösung: Es gibt für alle Entwickler eine einfache Regel:
All input is evil!
Jede Eingabe muss vom Programm gefiltert oder kontrolliert werden. Das bedeutet: Keine SQL-Befehle mehr aus Eingabefeldern zusammensetzen. Auch schon in ADO (Classic ASP) gab es Parameter-Übergabe, natürlich auch in ASP.NET. Diese verhindert, dass Texteingaben ungültige Zeichen enthalten und somit bösartige SQL-Befehle ausführen können!
Aus der Demo-Abfrage von oben wird:
select TOP (1) UserID from Users where username = @username and password = @password
und @username und @password werden als Parameter übergeben. In ASP.NET 2.0 (VB.NET) sieht das dann beispielsweise so aus:
Dim conn As SqlConnection
Dim cmd As New SqlCommand
conn = New SqlConnection(ConnectionStrings("conn").ConnectionString)
conn.Open()
cmd = New SqlCommand(SQL, conn)
cmd.CommandType = CommandType.Text ' od besser: .StoredProcedure
cmd.Parameters.AddWithValue("@username", Me.txtUsername.Text)
cmd.Parameters.AddWithValue("@password", Me.txtPassword.Text)
UserID = cmd.ExecuteScalar()
conn.Close()
und schon hat SQL-Injection dank der Parameter-Übergabe keine Chance mehr. Das Ganze noch in einen Try..Catch Block, mit etwas Fehlerabsicherung versehen und man hat sauberen, sicheren Code (Hardcore-Entwickler mögen mir verzeihen - ich weiß, der Code kann effizienter formuliert werden aber das Beispiel dient nur zur Demonstration).
Ziel dieses Artikels ist, das Bewußtsein zu diesem (eigentlich sehr alten) Thema zu fördern, die Funktionsweise zu demonstrieren und Entwickler anzuhalten, ihre Software sicher zu programmieren, damit keine mißbräuchliche Verwendung von öffentlich zugänglichen Webseiten möglich ist. Es gibt dazu eine Fülle von Anleitungen und Hilfen.
Viel mehr zu diesem Thema
und weitere Hilfestellung, wie man sich davor schützen kann, finden Sie in den folgenden Links:
und weitere Infos:
Achja, es gibt natürlich auch zum Thema Programmieren im Internet einige weitere interessante Security-Themen: SQL Cross Site Scripting, Canonicalization Attacks (wußten Sie, wie viele Arten es gibt, eine URL anzugeben, zum Beispiel auch dezimal), Query String, Form, Cookie, HTTP Header-Manipulations und vieles mehr. Internet-Technologie ist toll, aber bietet eine Reihe von Angriffsmöglichkeiten.
Bei Tauchern heißt der Abschiedsgruß "Gut Luft". Bei Entwicklern sollte es heißen "Secure coding"! ;-)
Beitrag von Toni Pohl