Nette Documentation Preview

syntax
Bezpečnostní rizika
*******************

.[perex]
Databáze často obsahuje citlivá data a umožňuje provádět nebezpečné operace. Nette Database nabízí řadu bezpečnostních prvků. Klíčové je ale pochopení rozdílu mezi bezpečným a nebezpečným API.


SQL Injection
=============

SQL injection je nejzávažnější bezpečnostní riziko při práci s databází. Vzniká, když se neošetřený vstup od uživatele stane součástí SQL dotazu. Útočník může vložit vlastní SQL příkazy a tím získat nebo modifikovat data v databázi.

```php
// ❌ NEBEZPEČNÝ KÓD - zranitelný vůči SQL injection
$database->query("SELECT * FROM users WHERE name = '$_GET[name]'");

// Útočník může zadat například hodnotu: ' OR '1'='1
// Výsledný dotaz pak bude:
// SELECT * FROM users WHERE name = '' OR '1'='1'
// Což vrátí všechny uživatele!
```

Totéž se týká i Database Explorer:

```php
// ❌ NEBEZPEČNÝ KÓD
$table->where('name = ' . $_GET['name']);
$table->where("name = '$_GET[name]'");
```


Bezpečné parametrizované dotazy
===============================

Bezpečným způsobem vkládání hodnot do SQL dotazů jsou parametrizované dotazy. Nette Database nabízí několik způsobů jejich použití.


Zástupné otazníky
-----------------

Nejjednodušší způsob je použití zástupných otazníků:

```php
// ✅ Bezpečné parametrizované dotazy
$database->query('SELECT * FROM users WHERE name = ?', $_GET['name']);

// ✅ Bezpečná podmínka v Exploreru
$table->where('name = ?', $_GET['name']);
```

Totéž platí pro všechny další metody v Database Explorer, které umožňují vkládat výrazy se zástupnými otazníky a parametry.

.[warning]
Hodnoty musí být skalárního typu (string, int, float, bool) nebo null. Pokud by například `$_GET['name']` bylo pole, Nette Database by vložil do SQL všechny jeho prvky, což může být nežádoucí.


Pole hodnot
-----------

Pro příkazy INSERT, UPDATE nebo klauzule WHERE můžeme použít pole hodnot:

```php
// ✅ Bezpečný INSERT
$database->query('INSERT INTO users', [
	'name' => $_GET['name'],
	'email' => $_GET['email'],
]);

// ✅ Bezpečný UPDATE
$database->query('UPDATE users SET', [
	'name' => $_GET['name'],
	'email' => $_GET['email'],
], 'WHERE id = ?', $_GET['id']);
```

Nette Database automaticky escapuje všechny hodnoty předané přes parametrizované dotazy. Musíme však zajistit správný datový typ parametrů.


Klíče polí nejsou bezpečné API
==============================

Zatímco hodnoty v polích jsou bezpečné, o klíčích to neplatí:

```php
// ❌ NEBEZPEČNÝ KÓD - klíče mohou obsahovat SQL injection
$database->query('INSERT INTO users', $_GET);
$database->query('SELECT * FROM users WHERE', $_GET);
$table->where($_GET);
```

U příkazů INSERT a UPDATE je to zásadní bezpečnostní chyba - útočník může do databáze vložit nebo změnit jakýkoliv sloupec. Mohl by si například nastavit `is_admin = 1` nebo vložit libovolná data do citlivých sloupců.

Ve WHERE podmínkách je to ještě nebezpečnější, protože umožňuje SQL enumeration - techniku postupného zjišťování informací o databázi. Útočník může třeba zkoumat plat zaměstnanců tím, že do `$_GET` podstrčí:

```php
$_GET = ['salary >', 100000];   // začne zjišťovat platové rozsahy
```

Ale hlavní problém je, že WHERE podmínky podporují v klíčích SQL výrazy:

```php
// Legitimní použití operátorů v klíčích
$table->where([
    'age > ?' => 18,
    'ROUND(score, ?) > ?' => [2, 75.5],
]);

// ❌ NEBEZPEČNÉ: útočník může vložit vlastní SQL
$_GET = ['1) UNION SELECT name, salary FROM users WHERE (is_admin = ?' => 1];
$table->where($_GET); // umožní útočníkovi získat platy adminů
```

Toto je opět **SQL injection**.


Whitelist sloupců
-----------------

Pokud chcete uživateli umožnit volbu sloupců, vždy použijte whitelist:

```php
// ✅ Bezpečné zpracování - pouze povolené sloupce
$allowedColumns = ['name', 'email', 'active'];
$values = array_intersect_key($_GET, array_flip($allowedColumns));

$database->query('INSERT INTO users', $values);
```


Dynamické identifikátory
========================

Pro dynamické názvy tabulek a sloupců použijte zástupný symbol `?name`:

```php
// ✅ Bezpečné použití důvěryhodných identifikátorů
$table = 'users';
$column = 'name';
$database->query('SELECT ?name FROM ?name', $column, $table);

// ❌ NEBEZPEČNÉ - nikdy nepoužívejte vstup od uživatele
$database->query('SELECT ?name FROM users', $_GET['column']);
```

Symbol `?name` používejte pouze pro důvěryhodné hodnoty definované v kódu aplikace. Pro hodnoty od uživatele použijte opět whitelist.

Bezpečnostní rizika

Databáze často obsahuje citlivá data a umožňuje provádět nebezpečné operace. Nette Database nabízí řadu bezpečnostních prvků. Klíčové je ale pochopení rozdílu mezi bezpečným a nebezpečným API.

SQL Injection

SQL injection je nejzávažnější bezpečnostní riziko při práci s databází. Vzniká, když se neošetřený vstup od uživatele stane součástí SQL dotazu. Útočník může vložit vlastní SQL příkazy a tím získat nebo modifikovat data v databázi.

// ❌ NEBEZPEČNÝ KÓD - zranitelný vůči SQL injection
$database->query("SELECT * FROM users WHERE name = '$_GET[name]'");

// Útočník může zadat například hodnotu: ' OR '1'='1
// Výsledný dotaz pak bude:
// SELECT * FROM users WHERE name = '' OR '1'='1'
// Což vrátí všechny uživatele!

Totéž se týká i Database Explorer:

// ❌ NEBEZPEČNÝ KÓD
$table->where('name = ' . $_GET['name']);
$table->where("name = '$_GET[name]'");

Bezpečné parametrizované dotazy

Bezpečným způsobem vkládání hodnot do SQL dotazů jsou parametrizované dotazy. Nette Database nabízí několik způsobů jejich použití.

Zástupné otazníky

Nejjednodušší způsob je použití zástupných otazníků:

// ✅ Bezpečné parametrizované dotazy
$database->query('SELECT * FROM users WHERE name = ?', $_GET['name']);

// ✅ Bezpečná podmínka v Exploreru
$table->where('name = ?', $_GET['name']);

Totéž platí pro všechny další metody v Database Explorer, které umožňují vkládat výrazy se zástupnými otazníky a parametry.

Hodnoty musí být skalárního typu (string, int, float, bool) nebo null. Pokud by například $_GET['name'] bylo pole, Nette Database by vložil do SQL všechny jeho prvky, což může být nežádoucí.

Pole hodnot

Pro příkazy INSERT, UPDATE nebo klauzule WHERE můžeme použít pole hodnot:

// ✅ Bezpečný INSERT
$database->query('INSERT INTO users', [
	'name' => $_GET['name'],
	'email' => $_GET['email'],
]);

// ✅ Bezpečný UPDATE
$database->query('UPDATE users SET', [
	'name' => $_GET['name'],
	'email' => $_GET['email'],
], 'WHERE id = ?', $_GET['id']);

Nette Database automaticky escapuje všechny hodnoty předané přes parametrizované dotazy. Musíme však zajistit správný datový typ parametrů.

Klíče polí nejsou bezpečné API

Zatímco hodnoty v polích jsou bezpečné, o klíčích to neplatí:

// ❌ NEBEZPEČNÝ KÓD - klíče mohou obsahovat SQL injection
$database->query('INSERT INTO users', $_GET);
$database->query('SELECT * FROM users WHERE', $_GET);
$table->where($_GET);

U příkazů INSERT a UPDATE je to zásadní bezpečnostní chyba – útočník může do databáze vložit nebo změnit jakýkoliv sloupec. Mohl by si například nastavit is_admin = 1 nebo vložit libovolná data do citlivých sloupců.

Ve WHERE podmínkách je to ještě nebezpečnější, protože umožňuje SQL enumeration – techniku postupného zjišťování informací o databázi. Útočník může třeba zkoumat plat zaměstnanců tím, že do $_GET podstrčí:

$_GET = ['salary >', 100000];   // začne zjišťovat platové rozsahy

Ale hlavní problém je, že WHERE podmínky podporují v klíčích SQL výrazy:

// Legitimní použití operátorů v klíčích
$table->where([
    'age > ?' => 18,
    'ROUND(score, ?) > ?' => [2, 75.5],
]);

// ❌ NEBEZPEČNÉ: útočník může vložit vlastní SQL
$_GET = ['1) UNION SELECT name, salary FROM users WHERE (is_admin = ?' => 1];
$table->where($_GET); // umožní útočníkovi získat platy adminů

Toto je opět SQL injection.

Whitelist sloupců

Pokud chcete uživateli umožnit volbu sloupců, vždy použijte whitelist:

// ✅ Bezpečné zpracování - pouze povolené sloupce
$allowedColumns = ['name', 'email', 'active'];
$values = array_intersect_key($_GET, array_flip($allowedColumns));

$database->query('INSERT INTO users', $values);

Dynamické identifikátory

Pro dynamické názvy tabulek a sloupců použijte zástupný symbol ?name:

// ✅ Bezpečné použití důvěryhodných identifikátorů
$table = 'users';
$column = 'name';
$database->query('SELECT ?name FROM ?name', $column, $table);

// ❌ NEBEZPEČNÉ - nikdy nepoužívejte vstup od uživatele
$database->query('SELECT ?name FROM users', $_GET['column']);

Symbol ?name používejte pouze pro důvěryhodné hodnoty definované v kódu aplikace. Pro hodnoty od uživatele použijte opět whitelist.