Sicherheitsrisiken
Datenbanken enthalten oft sensible Daten und ermöglichen gefährliche Operationen. Nette Database bietet eine Reihe von Sicherheitsfunktionen. Es ist jedoch wichtig, den Unterschied zwischen sicheren und unsicheren APIs zu verstehen.
SQL-Einschleusung
SQL-Injection ist das größte Sicherheitsrisiko bei der Arbeit mit Datenbanken. Sie tritt auf, wenn ungeprüfte Benutzereingaben Teil einer SQL-Abfrage werden. Ein Angreifer kann seine eigenen SQL-Befehle einschleusen und so Daten in der Datenbank erlangen oder verändern.
// ❌ UNSAFE CODE - anfällig für SQL-Injection
$database->query("SELECT * FROM users WHERE name = '$_GET[name]'");
// Der Angreifer kann etwas eingeben wie: ' OR '1'='1
// Die resultierende Abfrage wird sein:
// SELECT * FROM users WHERE name = '' OR '1'='1'
// Dies gibt alle Benutzer zurück!
Das Gleiche gilt für den Database Explorer:
// ❌ UNSAFE CODE
$table->where('name = ' . $_GET['name']);
$table->where("name = '$_GET[name]'");
Sichere parametrisierte Abfragen
Der sichere Weg, Werte in SQL-Abfragen einzufügen, sind parametrisierte Abfragen. Nette Database bietet mehrere Möglichkeiten, diese zu verwenden.
Platzhalter Fragezeichen
Die einfachste Methode ist die Verwendung von Platzhalterfragezeichen:
// ✅ Sichere parametrisierte Abfragen
$database->query('SELECT * FROM users WHERE name = ?', $_GET['name']);
// ✅ Sichere Bedingung im Explorer
$table->where('name = ?', $_GET['name']);
Das Gleiche gilt für alle anderen Methoden im Database Explorer, die das Einfügen von Ausdrücken mit Platzhalterfragezeichen und Parametern erlauben.
Werte müssen vom skalaren Typ sein (string
, int
, float
,
bool
) oder null
. Wenn z.B., $_GET['name']
ein Array ist, wird Nette Database alle seine
Elemente in die SQL-Abfrage aufnehmen, was unerwünscht sein kann.
Wert-Arrays
Für die Klauseln INSERT
, UPDATE
oder WHERE
können wir Wertefelder verwenden:
// ✅ Sicheres INSERT
$database->query('INSERT INTO users', [
'name' => $_GET['name'],
'email' => $_GET['email'],
]);
// ✅ Sicheres UPDATE
$database->query('UPDATE users SET', [
'name' => $_GET['name'],
'email' => $_GET['email'],
], 'WHERE id = ?', $_GET['id']);
Nette Database entschlüsselt automatisch alle Werte, die durch parametrisierte Abfragen übergeben werden. Allerdings muss der korrekte Datentyp der Parameter sichergestellt werden.
Array-Schlüssel sind keine sichere API
Während Werte in Arrays sicher sind, gilt dies nicht für Schlüssel:
// ❌ UNSAFE CODE - Schlüssel können SQL-Injection enthalten
$database->query('INSERT INTO users', $_GET);
$database->query('SELECT * FROM users WHERE', $_GET);
$table->where($_GET);
Für die Befehle INSERT
und UPDATE
ist dies eine kritische Sicherheitslücke – ein Angreifer
könnte jede Spalte in der Datenbank einfügen oder verändern. Zum Beispiel könnte er is_admin = 1
setzen oder
beliebige Daten in sensible Spalten einfügen.
Unter den Bedingungen von WHERE
ist dies sogar noch gefährlicher, weil es SQL enumeration erlaubt –
eine Technik, um schrittweise Informationen über die Datenbank abzurufen. Ein Angreifer könnte versuchen, die Gehälter von
Mitarbeitern zu erkunden, indem er $_GET
auf diese Weise infiltriert:
$_GET = ['salary >', 100000]; // beginnt mit der Festlegung von Gehaltsspannen
Das Hauptproblem ist jedoch, dass WHERE
Bedingungen SQL-Ausdrücke in Schlüsseln unterstützen:
// Legitime Verwendung von Operatoren in Schlüsseln
$table->where([
'age > ?' => 18,
'ROUND(score, ?) > ?' => [2, 75.5],
]);
// UNSAFE: Angreifer kann sein eigenes SQL einfügen
$_GET = ['1) UNION SELECT name, salary FROM users WHERE (is_admin = ?' => 1];
$table->where($_GET); // ermöglicht dem Angreifer, Admin-Gehälter zu erhalten
Dies ist wieder einmal SQL-Injektion.
Whitelisting-Spalten
Wenn Sie Benutzern die Auswahl von Spalten erlauben wollen, verwenden Sie immer eine Whitelist:
// ✅ Sichere Verarbeitung - nur erlaubte Spalten
$allowedColumns = ['name', 'email', 'active'];
$values = array_intersect_key($_GET, array_flip($allowedColumns));
$database->query('INSERT INTO users', $values);
Dynamische Bezeichner
Für dynamische Tabellen- und Spaltennamen verwenden Sie den Platzhalter ?name
:
// ✅ Sichere Verwendung von vertrauenswürdigen Identifikatoren
$table = 'users';
$column = 'name';
$database->query('SELECT ?name FROM ?name', $column, $table);
// ❌ UNSAFE - niemals Benutzereingaben verwenden
$database->query('SELECT ?name FROM users', $_GET['column']);
Das Symbol ?name
sollte nur für vertrauenswürdige Werte verwendet werden, die im Anwendungscode definiert sind.
Für vom Benutzer bereitgestellte Werte verwenden Sie wiederum eine Whitelist.