Nette Documentation Preview

syntax
Sicherheitsrisiken
******************

.[perex]
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 .[#toc-sql-injection]
=======================================

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.

```php
// ❌ 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:

```php
// ❌ UNSAFE CODE
$table->where('name = ' . $_GET['name']);
$table->where("name = '$_GET[name]'");
```


Sichere parametrisierte Abfragen .[#toc-safe-parameterized-queries]
===================================================================

Der sichere Weg, Werte in SQL-Abfragen einzufügen, sind parametrisierte Abfragen. Nette Database bietet mehrere Möglichkeiten, diese zu verwenden.


Platzhalter Fragezeichen .[#toc-placeholder-question-marks]
-----------------------------------------------------------

Die einfachste Methode ist die Verwendung von Platzhalterfragezeichen:

```php
// ✅ 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.

.[warning]
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 .[#toc-value-arrays]
--------------------------------

Für die Klauseln `INSERT`, `UPDATE` oder `WHERE` können wir Wertefelder verwenden:

```php
// ✅ 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 .[#toc-array-keys-are-not-a-safe-api]
============================================================================

Während Werte in Arrays sicher sind, gilt dies nicht für Schlüssel:

```php
// ❌ 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:

```php
$_GET = ['salary >', 100000];   // beginnt mit der Festlegung von Gehaltsspannen
```

Das Hauptproblem ist jedoch, dass `WHERE` Bedingungen SQL-Ausdrücke in Schlüsseln unterstützen:

```php
// 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 .[#toc-whitelisting-columns]
-------------------------------------------------

Wenn Sie Benutzern die Auswahl von Spalten erlauben wollen, verwenden Sie immer eine Whitelist:

```php
// ✅ 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 .[#toc-dynamic-identifiers]
=================================================

Für dynamische Tabellen- und Spaltennamen verwenden Sie den Platzhalter `?name`:

```php
// ✅ 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.

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.