-
-
Notifications
You must be signed in to change notification settings - Fork 282
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
149 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
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. | ||
|
||
Ke zranitelnosti dochází, když se hodnoty z nedůvěryhodného zdroje vkládají přímo do SQL řetězce: | ||
|
||
```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 zástupný otazníky parametry. | ||
|
||
|
||
Pole hodnot | ||
----------- | ||
|
||
Pro příkazy INSERT, UPDATE nebo klauzuje 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ů. | ||
|
||
.[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í. | ||
|
||
|
||
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 zjistit existenci citlivých sloupců a jejich obsah: | ||
|
||
```php | ||
$_GET = ['is_admin =', 1]; // zjistí, zda existuje sloupec is_admin | ||
$_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], | ||
]); | ||
|
||
// ❌ Proto toto je NEBEZPEČNÉ: | ||
$_GET = [ | ||
'id > 0 OR 1=1' => 1, // útočník může vložit vlastní SQL | ||
]; | ||
$table->where($_GET); // vygeneruje podmínku WHERE id > 0 OR 1=1 | ||
// což vrátí všechny řádky | ||
``` | ||
|
||
Toto **umožňuje SQL injection**. Proto vždy explicitně vyjmenujte sloupce, se kterými chcete pracovat: | ||
|
||
|
||
Whitelist sloupců | ||
----------------- | ||
|
||
Pro bezpečné zpracování vstupu od uživatele použijte whitelist povolených sloupců: | ||
|
||
```php | ||
// ✅ Bezpečné zpracování vstupu pomocí whitelistu | ||
$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']); | ||
``` |