Nette Documentation Preview

syntax
Biztonsági kockázatok
*********************

<div class=perex>

Az adatbázis gyakran tartalmaz érzékeny adatokat és lehetővé teszi veszélyes műveletek végrehajtását. A Nette Database biztonságos használatához kulcsfontosságú:

- Megérteni a különbséget a biztonságos és a nem biztonságos API között
- Paraméterezett lekérdezéseket használni
- Helyesen validálni a bemeneti adatokat

</div>


Mi az SQL Injection?
====================

Az SQL injection a legkomolyabb biztonsági kockázat az adatbázisokkal való munka során. Akkor keletkezik, ha a felhasználótól származó, nem kezelt bemenet az SQL lekérdezés részévé válik. A támadó saját SQL parancsokat illeszthet be, és ezzel:
- Jogosulatlan hozzáférést szerezhet az adatokhoz
- Módosíthatja vagy törölheti az adatokat az adatbázisban
- Megkerülheti az authentikációt

```php
// ❌ VESZÉLYES KÓD - sebezhető az SQL injection-nel szemben
$database->query("SELECT * FROM users WHERE name = '$_GET[name]'");

// A támadó például megadhatja a következő értéket: ' OR '1'='1
// Az eredményül kapott lekérdezés ez lesz: SELECT * FROM users WHERE name = '' OR '1'='1'
// Ami visszaadja az összes felhasználót
```

Ugyanez vonatkozik a [Database Explorer |explorer]-re is:

```php
// ❌ VESZÉLYES KÓD - sebezhető az SQL injection-nel szemben
$table->where('name = ' . $_GET['name']);
$table->where("name = '$_GET[name]'");
```


Paraméterezett lekérdezések
===========================

Az SQL injection elleni alapvető védekezés a paraméterezett lekérdezések használata. A Nette Database több módszert is kínál ezek használatára.

A legegyszerűbb módszer a **kérdőjeles helyettesítők** használata:

```php
// ✅ Biztonságos paraméterezett lekérdezés
$database->query('SELECT * FROM users WHERE name = ?', $name);

// ✅ Biztonságos feltétel az Explorerben
$table->where('name = ?', $name);
```

Ez érvényes minden további metódusra a [Database Explorerben |explorer], amelyek lehetővé teszik kifejezések beillesztését kérdőjeles helyettesítőkkel és paraméterekkel.

Az INSERT, UPDATE parancsokhoz vagy a WHERE záradékhoz az értékeket tömbben adhatjuk át:

```php
// ✅ Biztonságos INSERT
$database->query('INSERT INTO users', [
	'name' => $name,
	'email' => $email,
]);

// ✅ Biztonságos INSERT az Explorerben
$table->insert([
	'name' => $name,
	'email' => $email,
]);
```


Paraméterértékek validálása
===========================

A paraméterezett lekérdezések a biztonságos adatbázis-kezelés alapkövei. Azonban az értékeknek, amelyeket beléjük illesztünk, több ellenőrzési szinten kell átesniük:


Típusellenőrzés
---------------

**A legfontosabb a paraméterek helyes adattípusának biztosítása** - ez szükséges feltétele a Nette Database biztonságos használatának. Az adatbázis feltételezi, hogy minden bemeneti adat helyes adattípussal rendelkezik, amely megfelel az adott oszlopnak.

Például, ha az előző példákban a `$name` váratlanul egy tömb lenne egy string helyett, a Nette Database megpróbálná az összes elemét beilleszteni az SQL lekérdezésbe, ami hibához vezetne. Ezért **soha ne használjon** validálatlan adatokat a `$_GET`, `$_POST` vagy `$_COOKIE` tömbökből közvetlenül az adatbázis lekérdezésekben.


Formátumellenőrzés
------------------

A második ellenőrzési szinten az adatok formátumát ellenőrizzük - például, hogy a stringek UTF-8 kódolásúak-e, és hosszuk megfelel-e az oszlop definíciójának, vagy hogy a numerikus értékek az adott oszlop adattípusához megengedett tartományban vannak-e.

Ezen a validálási szinten részben magára az adatbázisra is támaszkodhatunk - sok adatbázis elutasítja az érvénytelen adatokat. Azonban a viselkedés eltérő lehet, némelyik csendben levághatja a hosszú stringeket, vagy a tartományon kívüli számokat.


Domain ellenőrzés
-----------------

A harmadik szint az alkalmazásspecifikus logikai ellenőrzéseket jelenti. Például annak ellenőrzése, hogy a select boxokból származó értékek megfelelnek-e a kínált lehetőségeknek, hogy a számok a várt tartományban vannak-e (pl. életkor 0-150 év), vagy hogy az értékek közötti kölcsönös függőségek értelmesek-e.


Ajánlott validálási módszerek
-----------------------------

- Használjon [Nette Űrlapokat |forms:], amelyek automatikusan biztosítják az összes bemenet helyes validálását
- Használjon [Presentereket |application:] és adja meg az adattípusokat a paramétereknél az `action*()` és `render*()` metódusokban
- Vagy implementáljon saját validálási réteget standard PHP eszközökkel, mint például a `filter_var()`


Biztonságos munka az oszlopokkal
================================

Az előző szakaszban megmutattuk, hogyan kell helyesen validálni a paraméterértékeket. Azonban az SQL lekérdezésekben tömbök használatakor ugyanolyan figyelmet kell fordítanunk a kulcsaikra is.

```php
// ❌ VESZÉLYES KÓD - a tömb kulcsai nincsenek kezelve
$database->query('INSERT INTO users', $_POST);
```

Az INSERT és UPDATE parancsoknál ez alapvető biztonsági hiba - a támadó bármilyen oszlopot beilleszthet vagy módosíthat az adatbázisban. Például beállíthatná az `is_admin = 1`-et, vagy tetszőleges adatokat illeszthetne be érzékeny oszlopokba (ún. Mass Assignment Vulnerability).

A WHERE feltételekben ez még veszélyesebb, mivel operátorokat tartalmazhatnak:

```php
// ❌ VESZÉLYES KÓD - a tömb kulcsai nincsenek kezelve
$_POST['salary >'] = 100000;
$database->query('SELECT * FROM users WHERE', $_POST);
// végrehajtja a WHERE (`salary` > 100000) lekérdezést
```

A támadó ezt a megközelítést használhatja a munkavállalók fizetésének szisztematikus kiderítésére. Például elkezdheti a 100 000 feletti fizetések lekérdezésével, majd az 50 000 alattiakkal, és a tartomány fokozatos szűkítésével felfedheti az összes munkavállaló hozzávetőleges fizetését. Ezt a támadástípust SQL enumeration-nek nevezik.

A `where()` és `whereOr()` metódusok még [sokkal rugalmasabbak |explorer#where], és támogatják az SQL kifejezéseket, beleértve az operátorokat és függvényeket a kulcsokban és értékekben. Ez lehetőséget ad a támadónak SQL injection végrehajtására:

```php
// ❌ VESZÉLYES KÓD - a támadó saját SQL-t illeszthet be
$_POST = ['0) UNION SELECT name, salary FROM users WHERE (1'];
$table->where($_POST);
// végrehajtja a WHERE (0) UNION SELECT name, salary FROM users WHERE (1) lekérdezést
```

Ez a támadás lezárja az eredeti feltételt a `0)` segítségével, saját `SELECT`-et csatol a `UNION` segítségével, hogy érzékeny adatokat szerezzen a `users` táblából, és szintaktikailag helyes lekérdezést zár le a `WHERE (1)` segítségével.


Oszlopok Whitelistje
--------------------

Az oszlopnevekkel való biztonságos munkához szükségünk van egy mechanizmusra, amely biztosítja, hogy a felhasználó csak az engedélyezett oszlopokkal dolgozhasson, és ne tudjon sajátokat hozzáadni. Megpróbálhatnánk észlelni és blokkolni a veszélyes oszlopneveket (blacklist), de ez a megközelítés megbízhatatlan - a támadó mindig kitalálhat egy új módszert a veszélyes oszlopnév beírására, amit nem láttunk előre.

Ezért sokkal biztonságosabb megfordítani a logikát, és explicit módon definiálni az engedélyezett oszlopok listáját (whitelist):

```php
// Oszlopok, amelyeket a felhasználó módosíthat
$allowedColumns = ['name', 'email', 'active'];

// Eltávolítjuk az összes nem engedélyezett oszlopot a bemenetből
$filteredData = array_intersect_key($userData, array_flip($allowedColumns));

// ✅ Most már biztonságosan használhatjuk a lekérdezésekben, például:
$database->query('INSERT INTO users', $filteredData);
$table->update($filteredData);
$table->where($filteredData);
```


Dinamikus azonosítók
====================

Dinamikus tábla- és oszlopnevekhez használja a `?name` helyettesítő szimbólumot. Ez biztosítja az azonosítók helyes escapelését az adott adatbázis szintaxisa szerint (pl. backtickek használatával MySQL-ben):

```php
// ✅ Megbízható azonosítók biztonságos használata
$table = 'users';
$column = 'name';
$database->query('SELECT ?name FROM ?name', $column, $table);
// Eredmény MySQL-ben: SELECT `name` FROM `users`
```

Fontos: a `?name` szimbólumot csak az alkalmazás kódjában definiált, megbízható értékekhez használja. Felhasználótól származó értékekhez használja újra a [whitelistet |#Oszlopok Whitelistje]. Ellenkező esetben biztonsági kockázatoknak teszi ki magát:

```php
// ❌ VESZÉLYES - soha ne használjon felhasználói bemenetet
$database->query('SELECT ?name FROM users', $_GET['column']);
```

Biztonsági kockázatok

Az adatbázis gyakran tartalmaz érzékeny adatokat és lehetővé teszi veszélyes műveletek végrehajtását. A Nette Database biztonságos használatához kulcsfontosságú:

  • Megérteni a különbséget a biztonságos és a nem biztonságos API között
  • Paraméterezett lekérdezéseket használni
  • Helyesen validálni a bemeneti adatokat

Mi az SQL Injection?

Az SQL injection a legkomolyabb biztonsági kockázat az adatbázisokkal való munka során. Akkor keletkezik, ha a felhasználótól származó, nem kezelt bemenet az SQL lekérdezés részévé válik. A támadó saját SQL parancsokat illeszthet be, és ezzel:

  • Jogosulatlan hozzáférést szerezhet az adatokhoz
  • Módosíthatja vagy törölheti az adatokat az adatbázisban
  • Megkerülheti az authentikációt
// ❌ VESZÉLYES KÓD - sebezhető az SQL injection-nel szemben
$database->query("SELECT * FROM users WHERE name = '$_GET[name]'");

// A támadó például megadhatja a következő értéket: ' OR '1'='1
// Az eredményül kapott lekérdezés ez lesz: SELECT * FROM users WHERE name = '' OR '1'='1'
// Ami visszaadja az összes felhasználót

Ugyanez vonatkozik a Database Explorer-re is:

// ❌ VESZÉLYES KÓD - sebezhető az SQL injection-nel szemben
$table->where('name = ' . $_GET['name']);
$table->where("name = '$_GET[name]'");

Paraméterezett lekérdezések

Az SQL injection elleni alapvető védekezés a paraméterezett lekérdezések használata. A Nette Database több módszert is kínál ezek használatára.

A legegyszerűbb módszer a kérdőjeles helyettesítők használata:

// ✅ Biztonságos paraméterezett lekérdezés
$database->query('SELECT * FROM users WHERE name = ?', $name);

// ✅ Biztonságos feltétel az Explorerben
$table->where('name = ?', $name);

Ez érvényes minden további metódusra a Database Explorerben, amelyek lehetővé teszik kifejezések beillesztését kérdőjeles helyettesítőkkel és paraméterekkel.

Az INSERT, UPDATE parancsokhoz vagy a WHERE záradékhoz az értékeket tömbben adhatjuk át:

// ✅ Biztonságos INSERT
$database->query('INSERT INTO users', [
	'name' => $name,
	'email' => $email,
]);

// ✅ Biztonságos INSERT az Explorerben
$table->insert([
	'name' => $name,
	'email' => $email,
]);

Paraméterértékek validálása

A paraméterezett lekérdezések a biztonságos adatbázis-kezelés alapkövei. Azonban az értékeknek, amelyeket beléjük illesztünk, több ellenőrzési szinten kell átesniük:

Típusellenőrzés

A legfontosabb a paraméterek helyes adattípusának biztosítása – ez szükséges feltétele a Nette Database biztonságos használatának. Az adatbázis feltételezi, hogy minden bemeneti adat helyes adattípussal rendelkezik, amely megfelel az adott oszlopnak.

Például, ha az előző példákban a $name váratlanul egy tömb lenne egy string helyett, a Nette Database megpróbálná az összes elemét beilleszteni az SQL lekérdezésbe, ami hibához vezetne. Ezért soha ne használjon validálatlan adatokat a $_GET, $_POST vagy $_COOKIE tömbökből közvetlenül az adatbázis lekérdezésekben.

Formátumellenőrzés

A második ellenőrzési szinten az adatok formátumát ellenőrizzük – például, hogy a stringek UTF-8 kódolásúak-e, és hosszuk megfelel-e az oszlop definíciójának, vagy hogy a numerikus értékek az adott oszlop adattípusához megengedett tartományban vannak-e.

Ezen a validálási szinten részben magára az adatbázisra is támaszkodhatunk – sok adatbázis elutasítja az érvénytelen adatokat. Azonban a viselkedés eltérő lehet, némelyik csendben levághatja a hosszú stringeket, vagy a tartományon kívüli számokat.

Domain ellenőrzés

A harmadik szint az alkalmazásspecifikus logikai ellenőrzéseket jelenti. Például annak ellenőrzése, hogy a select boxokból származó értékek megfelelnek-e a kínált lehetőségeknek, hogy a számok a várt tartományban vannak-e (pl. életkor 0–150 év), vagy hogy az értékek közötti kölcsönös függőségek értelmesek-e.

Ajánlott validálási módszerek

  • Használjon Nette Űrlapokat, amelyek automatikusan biztosítják az összes bemenet helyes validálását
  • Használjon Presentereket és adja meg az adattípusokat a paramétereknél az action*() és render*() metódusokban
  • Vagy implementáljon saját validálási réteget standard PHP eszközökkel, mint például a filter_var()

Biztonságos munka az oszlopokkal

Az előző szakaszban megmutattuk, hogyan kell helyesen validálni a paraméterértékeket. Azonban az SQL lekérdezésekben tömbök használatakor ugyanolyan figyelmet kell fordítanunk a kulcsaikra is.

// ❌ VESZÉLYES KÓD - a tömb kulcsai nincsenek kezelve
$database->query('INSERT INTO users', $_POST);

Az INSERT és UPDATE parancsoknál ez alapvető biztonsági hiba – a támadó bármilyen oszlopot beilleszthet vagy módosíthat az adatbázisban. Például beállíthatná az is_admin = 1-et, vagy tetszőleges adatokat illeszthetne be érzékeny oszlopokba (ún. Mass Assignment Vulnerability).

A WHERE feltételekben ez még veszélyesebb, mivel operátorokat tartalmazhatnak:

// ❌ VESZÉLYES KÓD - a tömb kulcsai nincsenek kezelve
$_POST['salary >'] = 100000;
$database->query('SELECT * FROM users WHERE', $_POST);
// végrehajtja a WHERE (`salary` > 100000) lekérdezést

A támadó ezt a megközelítést használhatja a munkavállalók fizetésének szisztematikus kiderítésére. Például elkezdheti a 100 000 feletti fizetések lekérdezésével, majd az 50 000 alattiakkal, és a tartomány fokozatos szűkítésével felfedheti az összes munkavállaló hozzávetőleges fizetését. Ezt a támadástípust SQL enumeration-nek nevezik.

A where() és whereOr() metódusok még sokkal rugalmasabbak, és támogatják az SQL kifejezéseket, beleértve az operátorokat és függvényeket a kulcsokban és értékekben. Ez lehetőséget ad a támadónak SQL injection végrehajtására:

// ❌ VESZÉLYES KÓD - a támadó saját SQL-t illeszthet be
$_POST = ['0) UNION SELECT name, salary FROM users WHERE (1'];
$table->where($_POST);
// végrehajtja a WHERE (0) UNION SELECT name, salary FROM users WHERE (1) lekérdezést

Ez a támadás lezárja az eredeti feltételt a 0) segítségével, saját SELECT-et csatol a UNION segítségével, hogy érzékeny adatokat szerezzen a users táblából, és szintaktikailag helyes lekérdezést zár le a WHERE (1) segítségével.

Oszlopok Whitelistje

Az oszlopnevekkel való biztonságos munkához szükségünk van egy mechanizmusra, amely biztosítja, hogy a felhasználó csak az engedélyezett oszlopokkal dolgozhasson, és ne tudjon sajátokat hozzáadni. Megpróbálhatnánk észlelni és blokkolni a veszélyes oszlopneveket (blacklist), de ez a megközelítés megbízhatatlan – a támadó mindig kitalálhat egy új módszert a veszélyes oszlopnév beírására, amit nem láttunk előre.

Ezért sokkal biztonságosabb megfordítani a logikát, és explicit módon definiálni az engedélyezett oszlopok listáját (whitelist):

// Oszlopok, amelyeket a felhasználó módosíthat
$allowedColumns = ['name', 'email', 'active'];

// Eltávolítjuk az összes nem engedélyezett oszlopot a bemenetből
$filteredData = array_intersect_key($userData, array_flip($allowedColumns));

// ✅ Most már biztonságosan használhatjuk a lekérdezésekben, például:
$database->query('INSERT INTO users', $filteredData);
$table->update($filteredData);
$table->where($filteredData);

Dinamikus azonosítók

Dinamikus tábla- és oszlopnevekhez használja a ?name helyettesítő szimbólumot. Ez biztosítja az azonosítók helyes escapelését az adott adatbázis szintaxisa szerint (pl. backtickek használatával MySQL-ben):

// ✅ Megbízható azonosítók biztonságos használata
$table = 'users';
$column = 'name';
$database->query('SELECT ?name FROM ?name', $column, $table);
// Eredmény MySQL-ben: SELECT `name` FROM `users`

Fontos: a ?name szimbólumot csak az alkalmazás kódjában definiált, megbízható értékekhez használja. Felhasználótól származó értékekhez használja újra a whitelistet. Ellenkező esetben biztonsági kockázatoknak teszi ki magát:

// ❌ VESZÉLYES - soha ne használjon felhasználói bemenetet
$database->query('SELECT ?name FROM users', $_GET['column']);