SQL-Injection
Viele Webentwickler sind sich nicht bewusst, wie SQL-Abfragen manipuliert
werden können und gehen davon aus, dass eine SQL-Abfrage ein
vertrauenswürdiges Kommando ist. Das heißt, dass SQL-Abfragen in der Lage
sind, Zugriffskontrollen und dadurch Standard-Authentifizierungs- und
Autorisierungsprüfungen zu umgehen, und manchmal können SQL-Abfragen
sogar Zugriff zu Kommandos auf Betriebssystemebene erlauben.
Direct SQL Command Injection ist eine Technik, wo ein Angreifer
SQL-Kommandos erstellt oder existierende verändert, um versteckte Daten
sichtbar zu machen, wertvolle Daten zu überschreiben oder sogar
gefährliche Kommandos auf Systemebene des Datenbank-Hosts auszuführen.
Dies wird durch die Anwendung erreicht, welche den Input des Benutzers
mit statischen Parametern kombiniert, um eine SQL-Abfrage zu erstellen.
Die folgenden Beispiele basieren - leider - auf wahren Begebenheiten.
Aufgrund der fehlenden Eingabevalidierung und dem Verbinden zum
Datenbankserver als ein Superuser oder jemand, der Benutzer anlegen kann,
kann ein Angreifer einen Superuser in Ihrer Datenbank anlegen.
Beispiel #1
Die Ergebnisliste in mehrere Seiten aufsplitten ... und Superuser anlegen
(PostgreSQL)
<?php
$offset = $argv[0]; // Vorsicht, keine Validierung der Eingabe!
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
$result = pg_query($conn, $query);
?>
Normale Benutzer klicken auf die 'nächste'- bzw. 'vorige'-Links, wo der
$offset in der
URL enthalten ist.
Das Skript erwartet, dass der ankommende
$offset
einen Dezimalwert enthält. Was aber, wenn jemand versucht einzubrechen,
indem er das folgende in einer
urlencode()'d Form an
die
URL anhängt:
In diesem Fall würde ihm das Skript einen Superuser-Zugang zur Verfügung
stellen. Beachten Sie, dass
0;
einen gültigen Offset
zur ursprünglichen Abfrage liefert, und sie beendet.
Hinweis:
Es ist eine übliche Technik, den SQL-Parser mit dem SQL-Kommentarzeichen
--
zu zwingen, den Rest der vom Entwickler
geschriebenen Abfrage zu ignorieren.
Eine gangbare Methode, an Passwörter zu gelangen, ist, Ihre Seiten mit
den Suchergebnissen zu umgehen. Der Angreifer braucht nur zu probieren,
ob irgendeine übertragene Variable, die in dem SQL-Statement verwendet
wird, nicht richtig gehandhabt wird. Diese Filter können gewöhnlich in
einem vorausgehenden Formular gesetzt werden, indem WHERE-,
ORDER BY-, LIMIT-
und OFFSET
-Klauseln in
SELECT
-Statements angepasst werden. Wenn Ihre
Datenbank das UNION
-Konstrukt unterstützt, kann der
Angreifer versuchen, eine komplette Abfrage an das Original anzuhängen,
um Passwörter aus einer beliebigen Tabelle aufzulisten. Die Verwendung
von verschlüsselten Passwortfeldern wird ausdrücklich empfohlen.
Beispiel #2
Artikel auflisten ... und ein paar Passwörter (beliebiger Datenbankserver)
<?php
$query = "SELECT id, name, inserted, size FROM products
WHERE size = '$size'";
$result = odbc_exec($conn, $query);
?>
Der statische Teil der Abfrage kann mit einem anderen
SELECT
-Statement kombiniert werden, welches alle
Passwörter preisgibt
Wenn diese Abfrage (mit dem
'
und
--
) einer der in
$query verwendeten
Variablen zugewiesen würde, wird das Abfrage-Biest erwachen.
Auch SQL-UPDATEs sind anfällig für Angriffe. Diese Abfragen sind
ebenfalls durch das Ändern und Anhängen einer komplett neuen Abfrage
gefährdet. Aber der Angreifer könnte auch mit der
SET
-Klausel herumspielen. In diesem Fall muss eine
Schemainformation vorhanden sein, um die Abfrage erfolgreich manipulieren
zu können. Diese kann durch Untersuchen der Variablennamen im Formular,
oder simpel mittels Brute-Forcing gesammelt werden. Es gibt nicht so
viele Namenskonventionen für Felder, welche Passwörter oder Benutzernamen
speichern.
Beispiel #3
Vom Zurücksetzen eines Passworts ... zum Erlangen von mehr Rechten
(beliebiger Datenbankserver)
<?php
$query = "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";
?>
Aber wenn ein böswilliger Benutzer den Wert
' or uid
like'%admin%
an
$uid übermittelt, um das
Administrator-Passwort zu ändern, oder einfach
$pwd
auf
hehehe', trusted=100, admin='yes
setzt, um mehr
Rechte zu erhalten, dann wird die Abfrage verdreht:
Ein erschreckendes Beispiel, wie der Zugriff auf Befehle auf
Betriebssystemebene bei manchen Datenbankservern erfolgen kann:
Beispiel #4 Angriff auf das Betriebssystem des Datenbank-Hosts (MSSQL-Server)
<?php
$query = "SELECT * FROM products WHERE id LIKE '%$prod%'";
$result = mssql_query($query);
?>
Wenn ein Angreifer den Wert
a%' exec master..xp_cmdshell 'net
user test testpass /ADD' --
auf
$prod
überträgt, wird
$query zu:
Der MSSQL-Server führt die SQL-Statements im Batch aus, inklusive eines
Kommandos um einen neuen Benutzer zur Datenbank der lokalen Konten
hinzuzufügen. Würde diese Anwendung als
sa
und der
MSSQLSERVER-Service mit genügend Rechten laufen, hätte der Angreifer nun
ein Konto, mit welchem er Zugriff auf diese Maschine hätte.
Hinweis:
Manche der obigen Beispiele sind an einen spezifischen Datenbankserver
gebunden. Das heißt jedoch nicht, dass nicht ein ähnlicher Angriff auf
andere Produkte möglich wäre. Ihr Datenbankserver könnte auf andere
Weise genauso verwundbar sein.
Bild mit freundlicher Genehmigung von
» xkcd
Techniken zur Vermeidung
Obwohl es offensichtlich ist, dass ein Angreifer zumindest ein wenig
Kenntnis der genutzten Datenbankarchitektur besitzen muss, um einen
erfolgreichen Angriff durchzuführen, ist es oft sehr einfach, diese
Informationen zu erhalten. Wenn die Datenbank zum Beispiel Teil eines
Open-Source- oder eines anderen öffentlich verfügbaren Softwarepakets
mit einer Standardinstallation ist, dann sind diese Informationen
vollkommen offen und frei zugänglich. Diese Informationen können auch
durch Closed-Source-Code preisgegeben werden - selbst wenn dieser
kodiert, verschleiert oder kompiliert ist - und sogar durch Ihren
ureigenen Code durch die Anzeige von Fehlermeldungen. Andere Methoden
beinhalten die Nutzung typischer Tabellen- und Spaltennamen. Ein
Login-Formular etwa, dass eine Tabelle 'users' mit den Spaltennamen
'id', 'username' und 'password' nutzt.
Diese Angriffe basieren hauptsächlich auf dem Ausnutzen des Codes, der
nicht mit Blick auf die Sicherheit geschrieben wurde. Vertrauen Sie nie
auf irgendeine Art von Eingabe, insbesondere wenn sie von der
Clientseite kommt, selbst wenn er von einer Auswahlbox, einem
versteckten Eingabefeld oder einem Cookie kommt. Das erste Beispiel
zeigt, dass solch eine tadellose Abfrage ein Desaster anrichten kann.
Abgesehen davon profitieren Sie von einer Protokollierung der Abfragen
entweder in Ihrem Skript oder durch die Datenbank selbst, falls sie
Protokollierung unterstützt. Klar, die Protokollierung kann keinen
schädlichen Versuch verhindern, aber sie kann helfen herauszufinden,
welche Anwendung umgangen wurde. Das Protokoll ist nicht von sich aus
nützlich, aber durch die in ihm enthaltenen Informationen, und je mehr
Details es enthält, desto besser ist es im Allgemeinen.