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.

verze: 4.0