PHP-Einfach.de
  • PHP Tutorial
  • MySQL Tutorial
  • Für Fortgeschrittene
  • Webhosting
  • Jobs
  • Forum

SQL-Injections

10. Februar 2020
  1. Home
  2. »
  3. Für Fortgeschrittene
  4. »
  5. PHP Sicherheit
  6. »
  7. SQL-Injections

SQL-Injections bezeichnet das Ausnutzen von Sicherheitslücken im Zusammenhang mit SQL-Datenbanken, die durch mangelnde Überprüfung von Eingabeparameter entstehen. Diese Art der Sicherheitslücke zählt zu eine der häufigsten und ist oftmals besonders kritisch, da Angreifer so an sensible Daten eurer Nutzer gelangen können. Wie bei den meistens anderen Sicherheitslücken auch gilt bei SQL-Injections der Merkspruch: Traue niemals den Eingaben von Benutzern.

Inhaltsverzeichnis

  • 1 Grundlagen SQL-Injections
  • 2 Schutz vor SQL-Injections
  • 3 Validierung von Eingaben

Grundlagen SQL-Injections

Um die Angriffe und den Schutz zu verdeutlichen Nutzen wir nachfolgenden die User-Tabelle aus unserem MySQL Tutorial (weitere Infos).

Ein häufiger Fehler vieler Programmierung ist die falsche Dynamisierung von SQL-Queries. Einem Script wird ein gewisser Wert übergeben, beispielsweise mittels GET-Parameter eine ID. Um nun den Datensatz mit der entsprechenden ID abzufragen wird oft ein Code wie folgt verwendet:

Der einfachste Gedanke wäre wie folgt (diese Variante ist nicht zu empfehlen):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
$pdo = new PDO('mysql:host=localhost;dbname=databasename', 'username', 'password');
 
if(isset($_GET['id'])) {
   $id = $_GET['id'];
} else {
   die("Bitte eine ?id übergeben");
}
 
$sql = "SELECT email, vorname, nachname FROM users WHERE id = $id";
foreach ($pdo->query($sql) as $row) {
   echo $row['vorname']." ".$row['nachname']."<br />";
   echo "E-Mail: ".$row['email']."<br /><br />";
}
?>

Das Problem liegt hier in Zeile 10. Ruft ihr die Seite mit folgendem Parameter auf:
seite.php?id=1 OR id > 1

So wird der folgende SQL Befehl erzeugt und an die Datenbank gesendet:

1
SELECT email, vorname, nachname FROM users WHERE id = 1 OR id > 1

Das Resultat davon ist die Ausgabe aller Benutzer statt nur einem einzelnen Nutzer.  Dies mag vielleicht nun nicht so tragisch sein, aber ein Angreifer kann nun viel Unfug treiben. Der Aufruf der Seite mittels:
seite.php?id=-1 UNION SELECT email, vorname, passwort AS nachname FROM user

Daraus wird der folgende SQL-Query gebaut und an die Datenbank gesendet:

1
SELECT email, vorname, nachname FROM users WHERE id =-1 UNION SELECT email, vorname, passwort AS nachname FROM user

Mittels der UNION-Anweisung werden zwei SQL-Queries miteinander verbunden. Der erste Query versucht die Information für den Nutzer '-1' zu finden, der nicht existiert. Der zweite Query fragt erneut eure User-Tabelle ab, aber in diesem Fall wird das Passwort-Feld umbenannt in das Nachname-Feld. Der nachfolgende Script, der ja den harmlosen Nachnamen ausgibt, gibt nun alle E-Mail-Adressen, Vornamen und Passwörter eurer User aus.

Durch diese SQL-Injection kann ein Angreifer an sämtliche Daten eurer Datenbank gelangen.

Nicht nur SELECT-Anweisungen sind gefährdet, sondern sämtliche Daten die ihr an die Datenbank sendet. So können auch UPDATE, INSERT und DELETE-Anweisungen entsprechend manipuliert werden. Habt ihr beispielsweise den folgenden SQL-Query:

1
$sql = "UPDATE user SET vorname='".$_POST['neuer_vorname']."' WHERE id = ".$_SESSION['userid'];

Hier kann ein Angreifer zwar nicht unbedingt die Session manipulieren, aber dieser kann den Wert von neuer_vorname manipulieren. Gibt er im Formularfeld beispielsweise folgenden Wert ein:

1
You got hacked', passwort='Hacker Passwort' WHERE id=1 --

So sieht der an die Datenbank gesendete SQL-Query wie folgt aus (angenommen der Angreifer hat die User-ID 374):

1
UPDATE user SET vorname='You got hacked', passwort='Hacker Passwort' WHERE id=1 -- ' WHERE id = 374

Die zwei Bindestriche -- kommentieren die nachfolgenden Anweisungen aus. Mit solch einer Eingabe lässt sich der Vorname und das Passwort von beliebigen Benutzern in eurer User-Tabelle verändern. So kann der Angreifer beispielsweise das Passwort von einem Administrator überschreiben und erhält dadurch Zugriff auf einen Administratoraccount, womit weiterer Schaden angerichtet werden kann.

Schutz vor SQL-Injections

Der beste Schutz vor SQL-Injections ist mittels Prepared Statements gegeben. Bei prepared Statements werden die Parameter, im den obigen Beispielen die ID oder der neue Vorname, seperat vom SQL-Query an die Datenbank gesendet. Dadurch ist gewährleistet, dass eingeschleuster Code in die Variable keinen Effekt auf den Query hat.

Es ist stark zu empfehlen, jede Query mit Nutzerdaten als Prepared Statement auszuführen. Versucht nicht in Versuchung zu kommen den SQL-Query selber zu konstruieren, da dies Fehleranfällig ist. Durch die (richtige) Verwendung von Prepared Statements seid ihr gegen SQL-Injections geschützt.

 

Validierung von Eingaben

Nach eigener Erfahrung lassen sich 98% der Queries mittels Prepared Statements konstruieren und sind somit sicher für SQL-Injections. Ein paar spezielle Queries existieren dennoch, bei denen man nicht auf Prepared Statements zurückgreifen kann. Möchte man dem Besucher z.B. erlauben eine Tabelle basierend auf frei wählbare Spalten zu durchsuchen, so steckt das dynamische Element in der Struktur des Queries und nicht im Parameter.

Eine unsichere Variante für solch eine Suchfunktion wäre:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'username', 'password');
 
$suchspalte = $_GET['suchspalte']; //z.B. die Spalte vorname
$suchwort = $_GET['suchwort']; //z.B. den Namen Max
 
$statement = $pdo->prepare("SELECT * FROM users WHERE ".$suchspalte." LIKE :vorname");
$statement->execute(array(':vorname' => "%$suchwort%"));  
while($row = $statement->fetch()) {
   echo $row['vorname']." ".$row['nachname']."<br />";
   echo "E-Mail: ".$row['email']."<br /><br />";
}
?>

Der Parameter suchwort ist zwar vor SQL-Injections geschützt, aber über den Parameter suchspalte kann ein Angreifer beliebigen Schaden anrichten. Dieses per Prepared Statement abzusichern ist leider nicht möglich. Deswegen müssen wir entweder fixe SQL-Queries nutzen oder den Wert von $suchspalte entsprechend validieren.

Eine sichere Variante ist die folgende:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'username', 'password');
 
$suchspalte = $_GET['suchspalte']; //z.B. die Spalte vorname
$suchwort = $_GET['suchwort']; //z.B. den Namen Max
 
$erlaubte_spalten = array('vorname', 'nachname', 'email');
if(!in_array($suchspalte, $erlaubte_spalten)) {
   die('Ungültiger Parameter für $suchwort');
}
 
$statement = $pdo->prepare("SELECT * FROM users WHERE ".$suchspalte." LIKE :suchwort");
$statement->execute(array(':suchwort' => "%$suchwort%"));  
while($row = $statement->fetch()) {
   echo $row['vorname']." ".$row['nachname']."<br />";
   echo "E-Mail: ".$row['email']."<br /><br />";
}
?>

Hier wird ein Array mit erlaubten Werten erzeugt und nur wenn wir solch einen Wert vorliegend haben, wird das weitere Script ausgeführt. Falls ein Angreifer versuchen würde einen anderen Wert für $suchspalte zu übergeben, würde unser Script mittels der Funktion die() abbrechen.

Autor: Nils Reimers
Zurück: Penetrationtesting für PHP
Weiter: Script-Beispiele

Für Fortgeschrittene

  • Objektorientierte Programmierung
  • PHP Sicherheit
    • Authentifizierung in PHP
    • Code Injection
    • Cross-Site-Request-Forgery (CSRF)
    • Cross-Site-Scripting (XSS) in PHP
    • Daten sicher speichern
    • Daten validieren
    • Penetrationtesting für PHP
    • SQL-Injections
  • Script-Beispiele
  • Codeschnipsel
  • Stellenmarkt
Mit freundlicher Unterstützung von:
  • Punkt191 Werbeagentur
  • Casinopilot24.com
  • Casino utan Spelpaus
  • Casino utan Spelpaus med Trustly
  • Neueonline-Casinos.com
  • toponlinecasinobonus.de
  • CasinoHEX.at
  • Decasinos.de
  • Privatkredit247.com
  • CasinoAdvisers.com
  • parhaatuudetkasinot.com
  • BitcoinBuster.com
  • CryptoCasinos.com
  • Casinofrog.com
  • Casino ohne Lizenz bei casinoandy

Hoster – Geringste Ausfallzeit

  1. webgo Ø 1 Min.
  2. netcup Ø 7 Min.
  3. Linevast Ø 11 Min.
  4. manitu Ø 14 Min.
  5. All-Inkl.com Ø 16 Min.
  6. dogado Ø 16 Min.
  7. bplaced Ø 19 Min.
  8. Hetzner Ø 20 Min.
  9. Host Europe Ø 20 Min.
  10. Strato Ø 22 Min.
» Mehr erfahren

Impressum | Datenschutz | Auf PHP-Einfach.de werben

© PHP-Einfach.de 2003 - 2021

Um dich beim Lernen von PHP und MySQL zu unterstützen verwenden wir Cookies. OK Weitere Infos
Privacy & Cookies Policy

Privacy Overview

This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Notwendige
immer aktiv

Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.

Nicht notwendige

Any cookies that may not be particularly necessary for the website to function and is used specifically to collect user personal data via analytics, ads, other embedded contents are termed as non-necessary cookies. It is mandatory to procure user consent prior to running these cookies on your website.