Tipps & Tutorials

SQL-Injektionen verhindern in PHP


Veröffentlicht am 02.09.2020 von DomainFactory

Eine der wichtigsten Angriffsmethoden im Web ist derzeit die sogenannte SQL Injection (SQLI). SQLI-Attacken machen laut Akamai, Betreiber des nach eigenen Angaben weltgrößten CDNs, weit mehr als die Hälfte aller Angriffe auf Webanwendungen aus. Injections allgemein führen auch die OWASP Top Ten an, eine Liste der wichtigsten Webbedrohungen, die vom Open Web Application Security Project veröffentlicht wird.

Arten von Webangriffen

So funktionieren SQL Injections

Bei einer SQL-Injektion werden über eine anfällige Webanwendung böswillige SQL-Befehle in den SQL-Interpreter des Datenbanksystems eingeschleust. Das geschieht vor allem über manipulierte Formulareingaben oder URLs. 

Hintergrund: Das Formular baut aus den User-Eingaben ein SQL-Statement zusammen. Dieses wird an den Interpreter geschickt, der die enthaltene Abfrage oder den Befehl ausführt. In URLs dienen dagegen Parameter von HTTP-Requests (GET-Parameter) dazu, Bestandteile von SQL-Statements zu übergeben, die dann von einem Skript ausgeführt werden. Wenn die Webanwendung diese Eingaben nicht prüft, können sich Angreifer das zu Nutze machen und den Interpreter für ihre Zwecke missbrauchen. 

Per SQL-Injektionen können Daten nicht nur ausgelesen, sondern gelöscht oder verändert und eventuell sogar beliebiger Code eingeschleust und später ausgeführt werden. Aber auch wenn der Server bei einem unerwarteten SQL-Statement nur einen Fehler zurückgibt, können Hacker daraus schon wertvolle Informationen gewinnen, die ihnen weiterhelfen.

In Formularen werden dabei vor allem Zeichen genutzt, die Sonderfunktionen für den SQL-Interpreter haben (Metazeichen). Dazu gehören u. a. Semikolon, Backslash, Hochkomma oder Doppel-Minus. Es kann aber unter Umständen auch direkt Code eingegeben werden, den der SQL-Interpreter versteht.

Beispiel 1: Einschleusen per Login-Formular

Ein Beispiel soll das Prinzip von SQLI-Angriffen veranschaulichen. Angenommen, ein Angreifer weiß, dass es für die Anwendung, die er hacken will, ein Konto mit dem Benutzernamen „user1“ gibt. Er geht davon aus, dass das Login-Formular aus den Eingaben in den Formularfeldern für Benutzer und Passwort eine SQL-Abfrage mit ungefähr der folgenden Struktur generiert:

 

SELECT * FROM table_users 
WHERE username = '$username' 
AND password = '$password';

 

Gibt der Angreifer nun in das Feld für den Benutzernamen zum Beispiel „user1‘;--“ ein, also den bekannten Usernamen plus Hochkomma, Semikolon und zwei Minus-Zeichen, lautet die resultierende Anweisung:

 

SELECT * FROM table_users 
WHERE username = 'user1';-- 
AND password = '$password';

 

SQL interpretiert die Folge „-- " (Minus, Minus, Leerzeichen) als Kommentareinleitung. Deshalb wird die die darauf folgende Passwortabfrage ignoriert. Der Angreifer könnte aber auch „user1“ als Benutzernamen sowie „' OR 1=1;-- “ als Passwort eingeben. Das ergäbe dann eine Anweisung wie:

 

SELECT * FROM table_users 
WHERE username = 'user1' 
AND password = '' OR 1=1;-- ';

 

Weil der zweite Teil der WHERE-Klausel („password = '' OR '1'='1'“) jetzt immer True ergibt, wird auch hier die Authentifizierung umgangen. Das „-- „ kommentiert in diesem Fall nur die automatisch generierten abschließenden Zeichen (hier Hochkomma + Semikolon) aus, damit es keine Syntax-Fehlermeldung gibt.

Beispiel 2: Einschleusen per URL

In URLs ist das Prinzip ähnlich. Die Rolle von Metazeichen übernehmen hier Schlüsselwörter wie uniondelete oder update, die in eine Parameter-Abfrage eingeschleust und vom System ausgewertet werden. Per union-Join kann etwa an eine reguläre Select-Abfrage eine zweite Abfrage angehängt werden, um interessante Daten wie z. B. Login-Infos abzugreifen.

Angenommen, die folgende URL erzeugt die darunter stehende SQL-Abfrage, die eigentlich Artikel von Autoren namens Smith zum Thema Security auflisten soll:

 

http://www.domain.tld/index.php?author=smith&subject=security
SELECT author, subject FROM articles 
WHERE author LIKE '%smith' 
AND subject = 'security';

 

Ein Angreifer könnte dann, vorausgesetzt, er weiß von einer Tabelle users, seine eigene Abfrage starten und sich eine Liste von Anmeldedaten aller Benutzer ausgeben lassen:

 

http://www.domain.tld/index.php?author=smith&subject=security
+UNION+SELECT+login,+password +FROM+users

 

mit der resultierenden Abfrage:

 

SELECT author, subject FROM articles 
WHERE author LIKE '%smith' AND subject = 'security' 
UNION SELECT login, password FROM users;

 

(„UNION SELECT login, password FROM users“ könnte aber auch direkt in ein Suchformular eingegeben werden.)

SQL-Injections verhindern: Prepared Statements

Am wirksamsten werden Injektionen vermieden, wenn benutzerdefinierte Daten grundsätzlich vom Interpreter ferngehalten werden. Dafür werden sogenannte Prepared Statements verwendet. Die wichtigsten SQL-Datenbanksysteme, darunter MySQL, MariaDB, PostgreSQL oder Microsoft SQL Server, unterstützen Prepared Statements. Wie der Name schon andeutet, wird dabei ein bereits kompiliertes Query-Template mit Platzhaltern für die Parameterwerte aufgerufen (prepare) und an PHP-Variablen gebunden (bind). Die eigentlichen Abfragedaten werden erst bei der Ausführung zur Laufzeit übergeben (execute). Weil so Struktur und Daten einer Abfrage getrennt übermittelt werden, sind keine Injektionen möglich. (Hier finden Sie Informationen zu Prepared Statements mit PHP Data Objects und mit MySQLi.)

Tipp: Auch die Verwendung von Object Relational Mapping (ORM) via Doctrine schützt vor SQL-Injektionen und macht Ihren Code unabhängig von dem genutzten Datenbanksystem.

Mehr Sicherheit und Stabilität: Input validieren

Eine Validierung des Benutzerinputs – oder besser, jeglichen Inputs aus nicht vertrauenswürdigen, von Fremden kontrollierbaren Quellen – wird durch Prepared Statements nicht überflüssig. Zwar können damit die meisten SQL-Injection-Angriffe vermieden werden, nicht aber alle unerwünschten Effekte (zum Beispiel Full-Table-Scans). Denn auch in gebundenen Parametern können etwa Metazeichen wie z. B. Wildcard-Zeichen (*, %, ? etc.) unter Umständen noch Wirkung zeigen, etwa bei der Verwendung von LIKE. Vor allem aber schützen Prepared Statements nicht gegen andere Angriffe, etwa mit bösartigem Javascript-Code.

Stellen Sie daher stets sicher, dass eingegebene Daten den Anforderungen zu ihrer Weiterverarbeitung entsprechen. Wie Sie das bewerkstelligen, lesen Sie in diesem Beitrag PHP-Sicherheit: Benutzereingaben validieren, maskieren und filtern

Schwachstellen schließen

Auch wenn die beschriebenen Maßnahmen die Erfolgswahrscheinlichkeit von SQLI-Angriffen stark reduzieren – 100prozentige Sicherheit gibt es nicht. Deshalb ist es trotzdem eine gute Idee, Ihre Website regelmäßig auf Schwachstellen zu scannen. Das übernimmt zum Beispiel Sucuri, die Website Security Suite von DomainFactory. Darüber hinaus wehrt Sucuri  neben vielen anderen Gefahren auch SQLI-Attacken ab (über zwei Millionen in 2019) und sorgt im Falle einer Infektion für eine sichere Bereinigung der Website.

Wie schütze ich meine Webseite - Tipps vom DomainFactory Support

Der Autor:


Als Qualitätsanbieter überzeugen wir mit HighEnd-Technologie und umfassenden Serviceleistungen. Mit mehr als 1,3 Millionen verwalteten Domainnamen gehören wir zu den größten Webhosting-Unternehmen im deutschsprachigen Raum.