Skip to content

Commit

Permalink
security
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Dec 17, 2024
1 parent c2c0f37 commit 76825ee
Showing 1 changed file with 149 additions and 0 deletions.
149 changes: 149 additions & 0 deletions database/cs/security.texy
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']);
```

0 comments on commit 76825ee

Please sign in to comment.