SQL-Injection: Erklärung und Schutzmaßnahmen

In großen und mittelständischen Unternehmen sind Datenbanksysteme unverzichtbar: So speichern und verwalten Banken und Versicherungen Konteninformationen und Buchungen strukturiert in den praktischen Speichersystemen. Viele Firmen planen ihre Ressourcen mit Enterprise-Resource-Planning-Systemen, die ihrerseits nicht ohne Datenbanken funktionieren. Und auch der Großteil bereitgestellter Angebote im World Wide Web wäre ohne Datenbanksysteme nicht realisierbar. Diese elektronischen Karteikästen einzurichten und zu pflegen, ist mit einem großen Aufwand verbunden – die größte Herausforderung besteht allerdings darin, ihre Sicherheit zu gewährleisten. Back-up-Verfahren und die Nutzung ausfallsicherer Hardware sind ebenso wichtig wie umfassende Schutzmaßnahmen gegen externe Zugriffe. Vor allem sogenannte SQL-Injections stellen für die traditionellen relationalen Datenbankmodelle und die implementierten Informationen eine große Gefahr dar.

Was ist eine SQL-Injection?

Unter einer SQL-Injection (dt. SQL-Einschleusung) versteht man das Ausnutzen einer Sicherheitslücke in relationalen Datenbankensystemen, die bei der Dateneingabe auf die Sprache SQL zurückgreifen. Der Angreifer macht sich dabei solche Benutzereingaben in die Datenbank-Oberflächen zunutze, die nicht ausreichend maskiert sind und Metazeichen wie den doppelten Bindestrich, Anführungszeichen, das Quote-Zeichen oder das Semikolon enthalten. Diese Zeichen besitzen Sonderfunktionen für den SQL-Interpreter und erlauben die externe Beeinflussung der ausgeführten Befehle. Oft tritt eine SQL-Injection in Zusammenhang mit PHP- und ASP-Programmen auf, die auf ältere Interfaces zurückgreifen. Hier erhalten die Eingaben in einigen Fällen nicht die notwendige Maskierung und sind damit das perfekte Ziel für einen Angriff.

Mit dem gezielten Einsatz von Funktionszeichen schleust ein eigentlich unberechtigter Benutzer auf diese Weise weitere SQL-Befehle ein und manipuliert die Einträge derart, dass er Daten verändern, löschen oder lesen kann. In gravierenden Fällen ist es sogar möglich, dass sich ein Angreifer auf diesem Wege den Zugriff auf die Kommandozeile des befehlsausführenden Systems und damit über den gesamten Datenbankserver verschafft.

SQL-Injection-Beispiele: So funktionieren die Datenbank-Angriffe

Da anfällige Datenbankserver schnell aufgespürt und SQL-Injection-Attacken ebenso einfach ausgeführt sind, gehört die Methode weltweit zu den beliebtesten. Dabei agieren die Kriminellen mit verschiedenen Angriffsmustern und machen sich so aktuelle, aber vor allem auch altbekannte Sicherheitslücken der am Datenmanagementprozess beteiligten Anwendungen zunutze. Um zu verdeutlichen, wie genau eine SQL-Injection funktioniert, haben wir einige der häufigsten Methoden beispielhaft für Sie zusammengetragen.

Beispiel 1: Zugriff über eine mangelhaft maskierte Benutzereingabe

Damit ein Benutzer auf eine Datenbank zugreifen kann, muss er sich für gewöhnlich zunächst authentifizieren. Zu diesem Zweck existieren Skripte, die beispielsweise ein Log-in-Formular bestehend aus Nutzername und Passwort präsentieren. Der User füllt das Formular aus und das Skript überprüft im Anschluss, ob in der Datenbank ein entsprechender Eintrag existiert. Standardmäßig sind hierfür in der Datenbank eine Tabelle mit dem Namen „users“ sowie den Zeilen „username“ und „password“ angelegt. Bei einer beliebigen Webapplikation könnten die betreffenden Skriptzeilen (Pseudocode) für den Webserver-Zugriff folgendermaßen lauten:

uname = request.POST['username']
passwd = request.POST['password']

sql = "SELECT id FROM users WHERE username='" + uname + "' AND password='" + passwd + "'"

database.execute(sql)

Ein Angreifer hat nun die Möglichkeit, per SQL-Injection unter anderem das Passwortfeld zu manipulieren, indem er beispielsweise password' OR 1='1 eingibt, was zu folgender SQL-Abfrage führt:

sql = "SELECT id FROM users WHERE username='' AND password='password' OR 1='1'

Auf diese Weise erhält er Fall Zugriff auf die gesamte Nutzertabelle der Datenbank, da das Passwort immer wahr ist (1='1'). Loggt er nun als Administrator ein, kann er beliebige Veränderungen an den Einträgen vornehmen. Alternativ kann auf dem gleichen Weg auch das Feld des Benutzernamens manipuliert werden.

Beispiel 2: Daten ausspähen per ID-Manipulation

Informationen aus einer Datenbank per ID abzufragen ist eine praktische und gängige Methode, allerdings auch ein mögliches Einfallstor für eine SQL-Injection. So weiß ein Webserver beispielsweise durch eine in der URL übermittelte ID-Angabe, welche Informationen er aus der Datenbank abrufen soll. Das dazu passende PHP-Skript sieht in etwa so aus:

<?php 
  #Datenbankabfrage anhand einer ID 
  $id = $_REQUEST['id'];
  $result = mysql_query("SELECT * from tabelle WHERE id=$id");
  
  # Anzeige des Ergebnisse ...
   ?>

Die erwartete URL hat die Form …/script.php?id=22. In dem aufgeführten Fall würde der Tabelleneintrag mit der ID 22 aufgerufen werden. Hat ein fremder Nutzer nun die Gelegenheit, diese anfragende URL zu manipulieren und schickt dem Webserver stattdessen die Anfrage …/script.php?id=22+or+1=1, bewirkt der daraus resultierende mysql_query-Aufruf, dass nicht nur der Eintrag mit der ID 22, sondern alle Daten ausgelesen werden:

SELECT * FROM tabelle WHERE id=22 or 1=1

Im nachfolgenden englischsprachigen Video werden weitere Beispiele von SQL-Injection ausführlich und leicht verständlich demonstriert:

Zur Anzeige dieses Videos sind Cookies von Drittanbietern erforderlich. Ihre Cookie-Einstellungen können Sie hier aufrufen und ändern.

So finden Angreifer gefährdete Datenbanksysteme

Prinzipiell ist jede Website und jede Webanwendung anfällig für eine SQL-Injection, sofern SQL als Datenbanksprache zum Einsatz kommt – denn allzu oft sorgen die Hersteller der Programme, die mit der Datenbank kommunizieren, nicht für ein ausreichendes Maß an Sicherheit. Entdeckte Schwachstellen bleiben in den Weiten des Word Wide Webs nicht lange geheim und so existieren beispielsweise Informationsseiten, die aktuelle Sicherheitslücken präsentieren und Kriminellen darüber hinaus auch gleich verraten, wie sie passende Webprojekte über die Google-Suche finden können. Dank standardmäßiger Fehlermeldungen ist schnell überprüft, ob die angegebenen Treffer auch tatsächlich ein potenzielles Angriffsziel darstellen. So kann man einer URL mit enthaltenem ID-Parameter einfach ein Apostroph anhängen (wie im folgenden Beispiel):

[Domainname].de/news.php?id=5‘

Eine angreifbare Website sendet dann eine Fehlermeldung zurück, die in etwa folgendermaßen lautet:

„Query failed: You have an error in your SQL Syntax …“

Zu Deutsch: „Abfrage gescheitert: Ihre SQL-Syntax ist fehlerhaft …” Mit ähnlichen Methoden lassen sich auch Spaltenanzahl, Tabellen- und Spaltennamen, SQL-Version oder gar Benutzer und Passwörter ausgeben. Mit größtenteils freien Tools lassen sich Suche und anschließende SQL-Injection darüber hinaus auch automatisieren. In jedem Fall ist das Ausnutzen bekannter Sicherheitslücken strafbar, insofern es sich nicht um Datenbanksysteme handelt, die einem selbst gehören und die man einem Sicherheitscheck unterziehen möchte.

Wie Sie Ihre Datenbank vor SQL-Injection schützen

Sie können verschiedene Maßnahmen einleiten, um SQL-Injection-Attacken auf Ihr Datenbanksystem zu verhindern. Dabei sollten Sie sich mit allen involvierten Komponenten – dem Server, den einzelnen Anwendungen sowie dem Datenbankmanagementsystem – auseinandersetzen.

Schritt 1: Die automatischen Eingaben der Applikationen überwachen

Prüfen und filtern Sie die Methoden und Parameter, die eingebundene Applikationen bei Eingaben in die Datenbank nutzen. Die übergebenen Daten sollten immer in dem erwarteten Datentyp vorliegen. Ist ein numerischer Parameter gefragt, können Sie selbigen zum Beispiel mithilfe eines PHP-Skriptes inklusive is_numeric()-Funktion überprüfen. Bei der Filterung heißt es, entsprechende Sonderzeichen zu ignorieren. Ein weiterer wichtiger Punkt ist es, dafür zu sorgen, dass die Anwendungen möglichst keine externen Fehlermeldungen ausgeben, die Informationen über das verwendete System oder die Strukturen der Datenbank verraten.

Mittlerweile gängige Praxis sind außerdem die sogenannten Prepared Statements, die Sie mit vielen Datenbankmanagementsystemen nutzen können. Diese vordefinierten Anweisungen dienten ursprünglich dazu, häufiger auftretende Abfragen auszuführen, verringern aufgrund ihrer Struktur jedoch auch das Risiko einer SQL-Injection. Denn die parametrisierten Anweisungen übermitteln den eigentlichen SQL-Befehl von den Parametern getrennt an die Datenbank. Erst das Datenbankmanagementsystem selbst führt beide im Anschluss zusammen und maskiert dabei die entscheidenden Sonderzeichen automatisch.

Schritt 2: Für einen umfassenden Server-Schutz sorgen

Die Sicherheit des Servers, auf dem Sie Ihr Datenbankmanagementsystem ausführen, spielt natürlich auch bei der SQL-Injection-Prävention eine große Rolle. An erster Stelle steht hierbei das Härten des Betriebssystems nach dem bekannten Muster:

  • Installieren bzw. aktivieren Sie nur solche Anwendungen und Dienste, die für das Betreiben der Datenbank relevant sind.
  • Löschen Sie alle Benutzerkonten, die Sie nicht benötigen.
  • Sorgen Sie dafür, dass alle relevanten System- und Programm-Updates installiert sind.

Je höher die Anforderungen sind, die an die Sicherheit Ihres Webprojektes geknüpft sind, desto eher sollten Sie den Einsatz von Intrusion-Detection-Systemen (IDS) oder Intrusion-Prevention-Systemen (IPS) in Erwägung ziehen. Diese arbeiten mit verschiedenen Erkennungssystemen, um Angriffe auf den Server frühzeitig zu erkennen, Warnungen auszugeben und im Falle von IPS auch automatisch entsprechende Gegenmaßnahmen einzuleiten. Als sinnvolle Schutzmaßnahme kann sich außerdem auch ein Application Layer Gateway herausstellen, das den Datenverkehr zwischen Anwendungen und Webbrowser direkt auf Applikationsebene überwacht.

Schritt 3: Datenbank härten und sichere Codes verwenden

Wie auch Ihr Betriebssystem sollte die Datenbank von sämtlichen irrelevanten Faktoren befreit und regelmäßig aktualisiert werden. Entfernen Sie zu diesem Zweck alle gespeicherten Prozeduren, die Sie nicht benötigen und deaktivieren Sie alle unnötigen Dienste und Benutzerkonten. Richten Sie einen speziellen Datenbank-Account ein, der allein für den Zugriff aus dem Web vorgesehen ist und mit minimalen Zugriffsrechten auskommt. Speichern Sie ferner alle sensiblen Daten wie zum Beispiel Passwörter in verschlüsselter Form in Ihrer Datenbank.

Im Sinne der Prepared Statements ist es dringend zu empfehlen, das PHP-Modul mysql nicht zu verwenden und stattdessen mysqli oder PDO zu wählen. Auf diese Weise können Sie sich zusätzlich auch mit sicheren Codes schützen. So verhindert beispielsweise die Funktion mysqli_real_escape_string() in PHP-Skripten, dass Sonderzeichen in der ursprünglichen Form an die SQL-Datenbank übergeben werden und maskiert selbige. Wenn Sie zum Beispiel folgende Codezeilen

$query = "SELECT * FROM users 
WHERE username= '" . $_POST['username'] . "' 
AND password= '" . $_POST['password'] . "'";

um die genannte Funktion erweitern,

$query = "SELECT * FROM users 
WHERE username= '" . mysqli_real_escape_string($_POST['username']) . "' 
AND password= '" . mysql_real_escape_string($_POST['password']) . "'";

werden problematische Zeichen der Benutzereingabe durch die sichere SQL-Variante (\') ersetzt.

Auf der Seite bobby-tables.com hat man sich anhand eines xkcd-Webcomics mit der Thematik einladender Datenbank-Benutzereingaben auseinandergesetzt. Der Comic zeigt eine Mutter, die einen Anruf von der Schule ihres Sohnes – liebevoll Klein Bobby Tables genannt – erhält. Sie bejaht die Frage, ob ihr Sohn tatsächlich Robert'); DROP TABLE Students;–– hieße und erfährt im Anschluss den Hintergrund für den Anruf: Der Versuch, einen Eintrag für Robert in der Schülerdatenbank zu erstellen, führte dazu, dass der komplette Datenbestand gelöscht wurde. Roberts Mutter hat daraufhin nur wenig tröstende Worte übrig und äußert ihre Hoffnung, dass die Schule aus dem Fehler gelernt hat und die Eingaben in die Datenbank zukünftig bereinigen wird.

Der Comic soll verdeutlichen, welche fatalen Folgen unbedachte bzw. nicht überprüfte Benutzereingaben in eine Datenbank haben können. Im Fall der Schul-Datenbank verhält sich der Sachverhalt vermutlich in etwa folgendermaßen:

Die Namen der Schüler werden in einer Tabelle mit dem Namen Students gespeichert. Sobald ein neuer Schüler an die Schule kommt, wird er in diese Tabelle eingetragen, wobei der entsprechende Code etwa folgendermaßen aussieht:

$sql = "INSERT INTO Students (Name) VALUES ('" . $studentName . "');";
execute_sql($sql);

Es handelt sich hierbei um einen gewöhnlichen SQL-INSERT-Befehl, der den Inhalt der Variablen $studentName in die Tabelle Students einfügt. Durch den zweiten Teil des Codes wird die Anweisung an die Datenbank übermittelt (execute_sql). Für einen Schüler namens Paul funktioniert die SQL-Anweisung auch wie gewünscht. Der entsprechende Code

INSERT INTO Students (Name) VALUES ('Paul');

implementiert Paul in die Students-Tabelle. Nun sollte aber mit derselben Anweisung der Eintrag für Bobby Tables gemacht werden, was zu folgender Codezeile führte:

INSERT INTO Students (Name) VALUES ('Robert'); DROP TABLE Students;––');

Roberts Eintrag wurde auf diese Weise zwar der Tabelle hinzugefügt, die im Namen enthaltene DROP-TABLE-Anweisung sorgte allerdings dafür, dass die gesamte Tabelle im Anschluss gelöscht wurde. Aufgrund der nicht-maskierten Eingabe in die Datenbank führte die SQL-Injection der Mutter, die sich hinter dem Namen des kleinen Bobby Tables verbirgt, zum Erfolg.

Die im Comic vorgetragene Lösung, den Code eigenhändig zu bereinigen, ist allerdings nicht wirklich zu empfehlen. Da die manuelle Zeichenmaskierung nämlich sehr fehleranfällig ist, sollten Sie stattdessen die zuvor aufgeführten Lösungen wie parametrisierte Anweisungen oder maskierende Funktionen wie mysqli_real_escape_string() bevorzugen, um Ihre Datenbank vor den tückischen Angriffen zu schützen und eine SQL-Injection zu verhindern.