[DE] Unterschätzte Gefahr: SQL-Injections
Ich war immer der Meinung, dass die Zeit für SQL-Injections abgelaufen sei. Doch als ich vor wenigen Tagen hier auf steemit einen Post lesen musste, welcher auf fahrlässige Weise zeigt, wie man nicht mit SQL arbeiten sollte, wurde mir klar, dass dem nicht so ist. Dieser Post handelt nun also über SQL-Injections, was sie sind und wie man sich dagegen schützen kann.
Was sind SQL-Injections?
SQL-Injections sind eine Form des Angriffs auf datenbankbasierte Anwendungen. Nicht nur reine Web-Anwendungen, sondern auch alle Anwendungen, welche eine SQL-Datenbank integriert haben, sind davon betroffen[1]. Bekannte Beispiele für weitere Angriffsflächen sind Microsoft Access oder dBase von Borland. Der Zweck hinter SQL-Injections ist die Manipulation der von der Applikation benutzten SQL-Datenbank[2]. So können unauthentifizierte Zugriffe, das Löschen aller Daten oder der Diebstahl von Kreditkarteninformationen das Ziel der Angreifer sein. Meist erfolgen solche Angriffe über eine von der Applikation zur Verfügung gestellte Schnittstelle, welche nicht gegen schädliche Nutzereingabe gesichert wurde.
Ziele und Beispiele von SQL-Injections
Im vorherigen Kapitel bereits angesprochen, kann man mit SQL-Injections verschiedene Ziele verfolgen. In den folgenden Unterkapiteln möchte ich auf ein paar mögliche Ziele eingehen und zeige dazu, wie ein solcher Angriff aussehen könnte.
Bevor man sich jedoch einem solchen Angriff widmen kann, muss man sich sein Opfer genau vorstellen können. In unserem Fall wird es sich beispielsweise um eine webbasierte Anwendung handeln. Diese kann verallgemeinert aus folgenden Komponenten bestehen:
Komponente | Aufgabe |
---|---|
Client | Ist die Oberfläche, welche dem Benutzer eine Schnittstelle zum Server bietet |
Server | Liefert dem Client Ressourcen und ist die Schnittstelle zwischen Client und Datenbank |
Datenbank | Beinhaltet Datensätze und ist Ziel unseres Angriffes |
Wenn nun der Code, welcher für die Kommunikation zwischen dem Server und der Datenbank zuständig ist, Schwachstellen in der Implementierung aufweist und zum Beispiel jede Anfrage des Nutzers ungefiltert an die Datenbank weiterleitet, kann es zu schwerwiegenden Eingriffen kommen.
Das gewöhnliche "Login-Query"
Betrachten wir einen gewöhnlichen SQL-Befehl, um in einer Tabelle mit Nutzern nach einem speziellen Nutzer mit Name und Passwort zu suchen: SELECT * FROM users WHERE name='@sidem' AND password='qwertz';
. Wird nun ein Nutzer mit Namen @sidem
und dem Passwort qwertz
gefunden, wird er zurück gegeben. Für die folgenden Beispiele werden wir diesen SQL-Befehl als Grundlage der Login-Routine verwenden, welche wir angreifen wollen.
Authentifizierung umgehen
Möchten wir uns nun Zugriff zu einem geschützten Bereich verschaffen, haben wir mehrere verschiedene Möglichkeiten dies zu tun:
Authentifizierung als willkürlicher Benutzer
Die erste Möglichkeit besteht darin, dass wir uns als willkürlicher Benutzer versuchen zu authentifizieren. Das bedeutet, dass es für uns keine Rolle spielt, mit welchem Nutzer wir angemeldet werden und oder welche Rechte dieser besitzt. Damit wir dieses Ziel erreichen, müssen wir dafür sorgen, dass der SQL-Befehl unserer Login-Routine wie folgt abgeändert wird:
SELECT * from users WHERE name='' AND password='' OR 1=1;
Die kleine Änderung OR 1=1
nenn man Tautologie und bedeutet, dass die Bedingung für sämtliche Datensätze erfüllt ist.
Authentifizierung als bestimmter Benutzer
Die zweite Möglichkeit besteht darin, dass wir uns als bestimmter Benutzer versuchen zu authentifizieren. Dazu müssen wir den SQL-Befehl abermals wie folgt abändern:
SELECT * from users WHERE name='' AND password='' OR name='@security101';
Entscheidend bei diesem Befehl ist abermals der hintere Teil der Bedingung OR name='@security101'
. Mit dieser Änderung, wird dafür gesorgt, dass egal welches Passwort auch eingegeben wurde, der Datensatz des Nutzers mit dem Namen @security101 zurückgegeben wird.
Angriff auf ein unbekanntes System
Doch wie geht man vor, wenn man ein unbekanntes System vor sich hat? Wenn man nicht den internen Code zur Verfügung hat?
Nur gut, dass wir damals diesen Post noch nicht kannten und somit sicherlich scheunentorgroße Lücken in der Implementierung haben.
Schritt 1: Testen auf Lücken
Der erste Schritt ist denkbar einfach. Wir müssen einen Fehler auf Datenbank-Ebene provozieren. Dazu probieren wir es mit in SQL verwendeten Sonderzeichen, in unserem Fall mit einem Apostroph '
. Bekommen wir nun eine Fehlermeldung wie in dem Bild gezeigt, bedeutet dies, dass die Anwendung angreifbar ist.
Schritt 2: Informationen sammeln
Ein essentieller Teil beim Pentesting (oder auch hacken), ist das Sammeln von Informationen. In unserem Fall möchten wir wissen, aus wie vielen Spalten die Tabelle besteht, welche mit dem Login-Formular in Verbindung steht. Dazu benutzen wir den SQL-Befehl ORDER BY x
, welcher die Datensätze nach der xten Spalte sortieren soll. Wir ersetzen x
inkrementell durch einen höheren Integer-Wert, bis wir abermals einen Fehler erhalten. Somit wissen wir dann, dass die Tabelle aus x - 1
Spalten besteht.
Diese Information ist nützlich für
Schritt 3: Ausführung
Möchten wir in der Anwendung andere Daten ausgeben lassen, als die Anwendung es vielleicht vorsieht, können wir dazu den Befehl UNION ALL
benutzen. Dieser vereint die Ergebnismengen zweier Abfragen. Nutzen wir diesen mit zum Beispiel SELECT 1,2,3,4
würden wir in unserer unbekannten Anwendung folgendes erhalten:
Daraus lässt sich schließen, dass name
die Spalte Nummer 2 ist und, dass offenbar Adress-Daten in Spalte 4 stehen.
Mit diesem Wissen lässt sich nun jede beliebe Information an diese Stellen der Applikation einpflanzen. Zum Beispiel die Versionsnummer der Datenbank mit UNION ALL SELECT 1,@@Version,3,4
.
An diesem Punkt des Angriffs, sind der Fantasie keine Grenzen mehr gesetzt. So kann je nach Berechtigung des Datenbanknutzers die Datenbank mit DROP DATABASE testdb
gelöscht werden, nachdem man mit UNION ALL SELECT 1,group_concat(SCHEMA_NAME),3,4FROM information_schema.SCHEMATA
den Datenbanknamen erfahren hat.
Schutzmaßnahmen
Wie man eine Anwendung angreift wissen wir nun. Nun geht es darum, wie man diese vor SQL-Injections schützen kann.
Vorher möchte ich darauf hinweisen, dass es KEINEN 100%igen Schutz geben kann. Alle hier erwähnten Schutzmaßnahmen tragen lediglich dazu bei, die allgemeine Sicherheit zu erhöhen.
Prepared-Statements
Prepared-Statements sind vorbereitete Befehle mit Platzhaltern anstelle von Variablen. Der Zweck solcher Befehle liegt in der Optimierung der Verarbeitung. Der Befehl selbst und die dazugehörigen Parameter werden getrennt versendet. Dadurch entsteht der Vorteil, dass bei häufigen Aufrufen desselben Befehls lediglich die Parameter neu gesendet werden müssen und die Befehle dadurch schneller ausgeführt werden können.
Für den Sicherheitsaspekt helfen Prepared-Statements, da sie die Parameter auf Datentypen überprüfen und dabei Sonderzeichen und eventuell auftretende SQL-Befehle entfernen.
Rechteverwaltung
In unserem Beispiel-Angriff haben wir die Datenbank testdb
löschen können, da der jeweilige Datenbanknutzer, mit welchem dieser Befehl ausgeführt wurde, die Berechtigung dazu hatte. Hat man nun Rechtegruppen, welche so aufgeteilt werden, dass sie nur die Rechte besitzen, die von der Applikation benötigt werden, kann man potentielle Gefahren abwenden.
Fazit
Das Entwickeln von gegen SQL-Injections geschützten Anwendungen ist mindestens so einfach wie die Ausführung einer einfachen SQL-Injection selbst. Somit sollte es keinen Grund geben seine Anwendung nicht abzusichern, wobei gerade Prepared-Statements sogar noch einen kleinen Geschwindigkeitsvorteil mit liefern.
Quellen
[1]http://archiv.infsec.ethz.ch/education/projects/archive/sqliadReport.pdf (besucht am 09.02.2018 11:10)
[2]https://www.barracuda.com/glossary/sql-injection (besucht am 09.02.2018 11:15)
Dieser Posts ist eine Aufarbeitung einer wissenschaftlichen Arbeit, welche mit @patlongus geschrieben wurde.
Du hast Fragen, Änderungsvorschläge oder Wünsche? Lass es mich in den Kommentaren wissen 😉
In dem Sinne, frohes Coden.
This is a test comment, notify @kryzsec on discord if there are any errors please.
Being A SteemStem Member
Sehr interessanter Beitrag und Top geschrieben !
Erschreckend wie oft Datenbankanfragen nicht geschützt sind..
Dankeschön!
Vorallem ist es traurig, da es ja kein großer Mehraufwand ist es sicher zu implementieren.
Danke für diesen informativen Beitrag, aber das ist ein Thema mit dem ich null anfangen kann. Ich versteh nur Bahnhof. Werde ihn mir später nochmal des besseren Verständnisses wegen genauer durchlesen!
Gerne doch. Eventuell können dir deine Follower helfen, wenn du diesen Post resteemst 😉
Spaß beiseite: das Thema ist super wichtig, um als Entwickler keine Sicherheitslücken aufzureißen und als Nutzer keine unerwarteten Überraschungen zu erhalten (z.B. Kreditkartenklau).
PreparedStatements sind unglaublich nützlich, um SQL Injections zu umgehen. Sobald es um irgendwelche Nutzerdaten geht, verwende ich sie. Außerdem sind sie ganz einfach zu benutzen. Sobald man einmal über SQL Injections aufgeklärt wurde, unterläuft einem eigentlich kein Fehler mehr.
Da gebe ich dir recht.
Ist schon ein spannendes Thema. Die Dunkelziffern sind doch meistens sehr hoch, was wohl auch daran liegt das es keiner merkt.
Toller und Informativer post
Ja, das stimmt. Gerade wenn sich sich den Spaß macht irgendwo ein
'
einzufügen wird man häufig überrascht, dass man wirklich mit einem Fehler begrüßt wird.Das sorgt dann für Laune :P
Klasse Artikel. Vielen Dank!
Das thema ist super wichtig. Für Entwickler sowieso aber auch für den normalen Anwender. Denn nur wer eine Gefahr versteht kann sich entsprechend verhalten...
Dankeschön!
Ich teile deine Ansicht zu 100%. Aber leider ignorieren viele die Risiken...
Congratulations @drookyn! You have completed some achievement on Steemit and have been rewarded with new badge(s) :
Award for the number of upvotes
Click on any badge to view your own Board of Honor on SteemitBoard.
For more information about SteemitBoard, click here
If you no longer want to receive notifications, reply to this comment with the word
STOP