SQL-Injection: Erklärung und Schutzmaßnahmen

SQL-Injections stellen für relationale Datenbankmodelle und die darin gespeicherten Informationen eine große Gefahr dar. Ein umfassender Schutz gegen diese unbefugten externen Zugriffe, die durch Sicherheitslücken möglich werden, ist daher essenziell.

Was ist eine SQL-Injection?

Unter einer SQL-Injection (dt. SQL-Einschleusung) versteht man das Ausnutzen einer Sicherheitslücke in relationalen Datenbanksystemen, die bei der Dateneingabe auf die Abfragesprache SQL zurückgreifen. Der Angreifer bzw. die Angreiferin macht sich dabei solche Benutzereingaben zunutze, die nicht ausreichend maskiert sind und Metazeichen wie den doppelten Bindestrich, Anführungszeichen 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 im 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 kann ein unberechtigter User auf diese Weise weitere SQL-Befehle einschleusen und die Einträge derart manipulieren, 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 auf 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 werden können, gehört die Methode weltweit zu den beliebtesten. Dabei agieren die Kriminellen mit verschiedenen Angriffsmustern und machen sich aktuelle, aber vor allem auch altbekannte Sicherheitslücken der am Datenmanagementprozess beteiligten Anwendungen zunutze. Um zu verdeutlichen, wie genau eine SQL-Injection funktioniert, folgen exemplarisch zwei typische Methoden.

Beispiel 1: Zugriff über eine mangelhaft maskierte Benutzereingabe

Damit Benutzerinnen und Benutzer auf eine Datenbank zugreifen können, müssen sie sich für gewöhnlich zunächst authentifizieren. Zu diesem Zweck existieren Skripte, die beispielsweise ein Login-Formular bestehend aus Nutzername und Passwort präsentieren. User füllen das Formular aus und das Skript überprüft im Anschluss, ob in der Datenbank entsprechende Einträge existieren. Standardmäßig sind hierfür in der Datenbank eine Tabelle mit dem Namen users sowie den Spalten username und password angelegt. Bei einer beliebigen Webapplikation könnten die betreffenden Skriptzeilen (Python-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)
python

Ein Angreifer hat nun die Möglichkeit, das Passwortfeld per SQL-Injection gezielt 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'"
python

Auf diese Weise erhält er vollen Zugriff auf die gesamte Nutzertabelle der Datenbank, da das Passwort immer wahr ist (1='1'). Loggt er sich nun als Admin 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 erfährt 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
    $mysqli = new mysqli("localhost", "benutzername", "passwort", "datenbank");
    $id = intval($_GET['id']);
    $result = $mysqli->query("SELECT * FROM tabelle WHERE id=$id");
    while ($row = $result->fetch_assoc()) {
        echo print_r($row, true);
    }
?>
php

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 eine fremde Person 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 Aufruf, dass alle Daten der Tabelle ausgelesen werden:

SELECT * FROM tabelle WHERE id=22 OR 1=1;
sql

Wie finden Kriminelle gefährdete Datenbanksysteme?

Prinzipiell können alle Websites und Webanwendungen, die SQL-Datenbanken ohne vorbereitete Anfragen (Prepared Statements) oder andere Schutzmechanismen nutzen, anfällig für SQL-Injections sein. Entdeckte Schwachstellen bleiben in den Weiten des World 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. Falls eine Website detaillierte SQL-Fehlermeldungen zurückgibt, können Kriminelle diese nutzen, um potenzielle Schwachstellen zu identifizieren. 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 die Spaltenanzahl, Tabellen- und Spaltennamen, SQL-Version oder gar Nutzernamen und Passwörter ausgeben. Mit verschiedenen Tools lassen sich die Suche und anschließende SQL-Injections darüber hinaus auch automatisieren.

Wie Sie Ihre Datenbank vor SQL-Injection schützen

Sie können verschiedene Maßnahmen ergreifen, 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

Bei der Verarbeitung von Eingaben durch externe oder eingebundene Applikationen ist es essenziell, die übermittelten Werte zu validieren und zu filtern, um SQL-Injections zu verhindern.

1. Datentypen überprüfen

Jede Eingabe sollte dem erwarteten Datentyp entsprechen. Falls beispielsweise eine numerische Eingabe erforderlich ist, kann eine einfache Validierung in PHP so aussehen:

if (filter_var($input, FILTER_VALIDATE_INT) === false) {
    throw new InvalidArgumentException("Ungültige Eingabe");
}
php

Ähnliche Prüfungen sollten für Strings, Datumswerte oder andere spezifische Formate implementiert werden.

2. Sonderzeichen filtern

Spezielle Zeichen können Sicherheitslücken verursachen, insbesondere in SQL- oder HTML-Kontexten. Eine sichere Methode ist die Nutzung von htmlspecialchars() für HTML-Eingaben und von PDO::quote() für SQL-Anfragen.

3. Fehlermeldungen vermeiden

Direkte Fehlermeldungen, die technische Details über die Datenbank oder das System enthalten, sollten vermieden werden. Stattdessen empfiehlt sich eine generische Ausgabe wie:

echo "Ein Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.";
error_log("Unerwarteter Fehler aufgetreten. Weitere Details siehe Systemlog.");
php

4. Prepared Statements nutzen

Eine sichere Möglichkeit, SQL-Injections zu verhindern, sind die bereits erwähnten Prepared Statements. Hierbei werden SQL-Befehle und Parameter getrennt übermittelt, sodass schädlicher Code nicht ausgeführt werden kann. Ein passendes Beispiel in PHP mit PDO (PHP Data Objects) sieht wie folgt aus:

$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->bindParam(':id', $user_id, PDO::PARAM_INT);
$stmt->execute();
php

Das Datenbankmanagementsystem sorgt automatisch für eine sichere Verarbeitung der Eingaben.

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 den Betrieb 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.
  • Wenden Sie das Least-Privilege-Prinzip an, um sicherzustellen, dass User und Dienste nur die minimal erforderlichen Berechtigungen erhalten.

Je nach Sicherheitsanforderungen Ihres Webprojekts sollten zusätzliche Schutzmaßnahmen in Betracht gezogen werden:

  • Intrusion-Detection-Systeme (IDS) und Intrusion-Prevention-Systeme (IPS): Diese Systeme arbeiten mit verschiedenen Erkennungsmethoden, um Angriffe auf den Server frühzeitig zu erkennen, Warnungen auszugeben und im Falle von IPS auch automatisch entsprechende Gegenmaßnahmen einzuleiten.
  • Application Layer Gateway (ALG): Ein ALG überwacht und filtert den Datenverkehr zwischen Anwendungen und Webbrowsern direkt auf Applikationsebene.
  • Web Application Firewall (WAF): Eine WAF schützt Webanwendungen gezielt vor SQL-Injection und Cross-Site-Scripting (XSS), indem sie verdächtige Anfragen blockiert oder entschärft.
  • Zero-Trust-Ansatz: Dieser moderne Sicherheitsansatz stellt sicher, dass jeder Zugriff, unabhängig von seiner Quelle, überprüft und verifiziert wird, bevor er zugelassen wird.
  • Firewall-Regelungen und Netzwerksegmentierung: Diese Maßnahmen sind elementar, um die Angriffsfläche langfristig zu minimieren.
  • Regelmäßige IT-Sicherheitsaudits und Penetrationstests: Diese helfen dabei, Schwachstellen frühzeitig zu erkennen und zu schließen.

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 ausschließlich für den Zugriff aus dem Web vorgesehen ist und mit minimalen Zugriffsrechten auskommt.

Im Sinne der Prepared Statements ist es dringend zu empfehlen, das PHP-Modul mysql nicht zu verwenden (seit PHP 7 entfernt) und stattdessen mysqli oder PDO zu wählen. Auf diese Weise können Sie sich zusätzlich auch mit sicheren Codes schützen. Eine sichere mysqli-Abfrage sieht beispielsweise folgendermaßen aus:

$mysqli = new mysqli("localhost", "benutzer", "passwort", "datenbank");
if ($mysqli->connect_error) die("Verbindung fehlgeschlagen");
$stmt = $mysqli->prepare("SELECT password FROM users WHERE username = ?");
$stmt->bind_param("s", $_POST['username']);
$stmt->execute();
$stmt->bind_result($hashedPassword);
if ($stmt->fetch() && password_verify($_POST['password'], $hashedPassword)) {
    echo "Login erfolgreich";
} else {
    echo "Falsche Zugangsdaten";
}
$stmt->close();
$mysqli->close();
php

Zudem sollten Passwörter niemals direkt in einer Datenbank gespeichert oder als Klartext abgefragt werden. Verwenden Sie stattdessen eine Hashing-Methode wie password_hash() in Kombination mit password_verify(), um die Kennwörter optimal zu schützen. Eine sichere Lösung könnte folgendermaßen aussehen:

$mysqli = new mysqli("localhost", "benutzer", "passwort", "datenbank");
$stmt = $mysqli->prepare("SELECT password FROM users WHERE username = ?");
$stmt->bind_param("s", $_POST['username']);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
if ($row && password_verify($_POST['password'], $row['password'])) {
    echo "Login erfolgreich!";
} else {
    echo "Falscher Benutzername oder falsches Passwort.";
}
php

Bobby Tables: SQL-Injection im Comicstil erklärt

Auf der Seite bobby-tables.com hat man sich anhand eines xkcd-Webcomics mit der Thematik unsicherer 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 zeigt eindrücklich, welche fatalen Folgen nicht überprüfte Benutzereingaben in Datenbanken haben können.

War dieser Artikel hilfreich?
Page top