Nette Documentation Preview

syntax
Риск за сигурността
*******************

.[perex]
Базите данни често съдържат чувствителни данни и позволяват опасни операции. Базата данни Nette предоставя редица функции за сигурност. От решаващо значение е обаче да се разбере разликата между безопасни и опасни API.


SQL инжектиране .[#toc-sql-injection]
=====================================

SQL инжектирането е най-сериозният риск за сигурността при работа с бази данни. То възниква, когато непроверен потребителски вход стане част от SQL заявка. Атакуващият може да инжектира свои собствени SQL команди, като по този начин получава или променя данни в базата данни.

```php
// ❌ БЕЗОПАСЕН КОД - уязвим към SQL инжекция
$database->query("SELECT * FROM users WHERE name = '$_GET[name]'");

// Нападателят може да въведе нещо подобно на: ' ИЛИ '1'='1
// Получената заявка ще бъде:
// SELECT * FROM users WHERE name = '' OR '1'='1'
// Това връща всички потребители!
```

Същото се отнася и за Database Explorer:

```php
// ❌ КОД ЗА БЕЗОПАСНОСТ
$table->where('name = ' . $_GET['name']);
$table->where("name = '$_GET[name]'");
```


Безопасни параметризирани заявки .[#toc-safe-parameterized-queries]
===================================================================

Безопасният начин за вмъкване на стойности в SQL заявките е чрез параметризирани заявки. Базата данни Nette предоставя няколко начина за използването им.


Заместващи въпросителни знаци .[#toc-placeholder-question-marks]
----------------------------------------------------------------

Най-простият метод е да се използват заместващи въпросителни знаци:

```php
// ✅ Безопасни параметризирани заявки
$database->query('SELECT * FROM users WHERE name = ?', $_GET['name']);

// ✅ Безопасно условие в Explorer
$table->where('name = ?', $_GET['name']);
```

Същото важи и за всички други методи в Database Explorer, които позволяват вмъкване на изрази с въпросителни знаци и параметри.

.[warning]
Стойностите трябва да са от скаларен тип (`string`, `int`, `float`, `bool`) или `null`. Ако например `$_GET['name']` е масив, Nette Database ще включи всички негови елементи в SQL заявката, което може да е нежелателно.


Масиви от стойности .[#toc-value-arrays]
----------------------------------------

За клаузите `INSERT`, `UPDATE` или `WHERE` можем да използваме масиви от стойности:

```php
// ✅ Безопасен INSERT
$database->query('INSERT INTO users', [
	'name' => $_GET['name'],
	'email' => $_GET['email'],
]);

// ✅ Безопасен UPDATE
$database->query('UPDATE users SET', [
	'name' => $_GET['name'],
	'email' => $_GET['email'],
], 'WHERE id = ?', $_GET['id']);
```

Nette Database автоматично ескапира всички стойности, предадени чрез параметризирани заявки. Трябва обаче да осигурим правилния тип данни на параметрите.


Ключовете на масива не са сигурен API .[#toc-array-keys-are-not-a-safe-api]
===========================================================================

Докато стойностите в масивите са безопасни, същото не може да се каже за ключовете:

```php
// ❌ БЕЗОПАСЕН КОД - ключовете могат да съдържат SQL инжекция
$database->query('INSERT INTO users', $_GET);
$database->query('SELECT * FROM users WHERE', $_GET);
$table->where($_GET);
```

За командите `INSERT` и `UPDATE` това е критичен недостатък в сигурността - атакуващият може да вмъкне или промени всяка колона в базата данни. Например, той може да зададе `is_admin = 1` или да вмъкне произволни данни в чувствителни колони.

В условията на `WHERE` това е още по-опасно, защото позволява **SQL enumeration** - техника за постепенно извличане на информация за базата данни. Нападателят може да се опита да проучи заплатите на служителите, като инжектира в `$_GET` по този начин:

```php
$_GET = ['salary >', 100000];   // започва да определя диапазони на заплатите.
```

Основният проблем обаче е, че `WHERE` условията поддържат SQL изрази в ключовете:

```php
// Законосъобразно използване на оператори в ключовете
$table->where([
    'age > ?' => 18,
    'ROUND(score, ?) > ?' => [2, 75.5],
]);

// ❌ БЕЗОПАСНО: атакуващият може да инжектира свой собствен SQL
$_GET = ['1) UNION SELECT name, salary FROM users WHERE (is_admin = ?' => 1];
$table->where($_GET); // позволява на нападателя да получи администраторски заплати
```

Това отново е **SQL инжекция**.


Създаване на бял списък на колоните .[#toc-whitelisting-columns]
----------------------------------------------------------------

Ако искате да разрешите на потребителите да избират колони, винаги използвайте бял списък:

```php
// ✅ Безопасна обработка - само разрешени колони
$allowedColumns = ['name', 'email', 'active'];
$values = array_intersect_key($_GET, array_flip($allowedColumns));

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


Динамични идентификатори .[#toc-dynamic-identifiers]
====================================================

За динамични имена на таблици и колони използвайте заместителя `?name`:

```php
// ✅ Безопасно използване на надеждни идентификатори
$table = 'users';
$column = 'name';
$database->query('SELECT ?name FROM ?name', $column, $table);

// ❌ UNSAFE - никога не използвайте потребителски вход
$database->query('SELECT ?name FROM users', $_GET['column']);
```

Символът `?name` трябва да се използва само за доверени стойности, дефинирани в кода на приложението. За стойности, предоставени от потребителя, отново използвайте бял списък.

Риск за сигурността

Базите данни често съдържат чувствителни данни и позволяват опасни операции. Базата данни Nette предоставя редица функции за сигурност. От решаващо значение е обаче да се разбере разликата между безопасни и опасни API.

SQL инжектиране

SQL инжектирането е най-сериозният риск за сигурността при работа с бази данни. То възниква, когато непроверен потребителски вход стане част от SQL заявка. Атакуващият може да инжектира свои собствени SQL команди, като по този начин получава или променя данни в базата данни.

// ❌ БЕЗОПАСЕН КОД - уязвим към SQL инжекция
$database->query("SELECT * FROM users WHERE name = '$_GET[name]'");

// Нападателят може да въведе нещо подобно на: ' ИЛИ '1'='1
// Получената заявка ще бъде:
// SELECT * FROM users WHERE name = '' OR '1'='1'
// Това връща всички потребители!

Същото се отнася и за Database Explorer:

// ❌ КОД ЗА БЕЗОПАСНОСТ
$table->where('name = ' . $_GET['name']);
$table->where("name = '$_GET[name]'");

Безопасни параметризирани заявки

Безопасният начин за вмъкване на стойности в SQL заявките е чрез параметризирани заявки. Базата данни Nette предоставя няколко начина за използването им.

Заместващи въпросителни знаци

Най-простият метод е да се използват заместващи въпросителни знаци:

// ✅ Безопасни параметризирани заявки
$database->query('SELECT * FROM users WHERE name = ?', $_GET['name']);

// ✅ Безопасно условие в Explorer
$table->where('name = ?', $_GET['name']);

Същото важи и за всички други методи в Database Explorer, които позволяват вмъкване на изрази с въпросителни знаци и параметри.

Стойностите трябва да са от скаларен тип (string, int, float, bool) или null. Ако например $_GET['name'] е масив, Nette Database ще включи всички негови елементи в SQL заявката, което може да е нежелателно.

Масиви от стойности

За клаузите INSERT, UPDATE или WHERE можем да използваме масиви от стойности:

// ✅ Безопасен INSERT
$database->query('INSERT INTO users', [
	'name' => $_GET['name'],
	'email' => $_GET['email'],
]);

// ✅ Безопасен UPDATE
$database->query('UPDATE users SET', [
	'name' => $_GET['name'],
	'email' => $_GET['email'],
], 'WHERE id = ?', $_GET['id']);

Nette Database автоматично ескапира всички стойности, предадени чрез параметризирани заявки. Трябва обаче да осигурим правилния тип данни на параметрите.

Ключовете на масива не са сигурен API

Докато стойностите в масивите са безопасни, същото не може да се каже за ключовете:

// ❌ БЕЗОПАСЕН КОД - ключовете могат да съдържат SQL инжекция
$database->query('INSERT INTO users', $_GET);
$database->query('SELECT * FROM users WHERE', $_GET);
$table->where($_GET);

За командите INSERT и UPDATE това е критичен недостатък в сигурността – атакуващият може да вмъкне или промени всяка колона в базата данни. Например, той може да зададе is_admin = 1 или да вмъкне произволни данни в чувствителни колони.

В условията на WHERE това е още по-опасно, защото позволява SQL enumeration – техника за постепенно извличане на информация за базата данни. Нападателят може да се опита да проучи заплатите на служителите, като инжектира в $_GET по този начин:

$_GET = ['salary >', 100000];   // започва да определя диапазони на заплатите.

Основният проблем обаче е, че WHERE условията поддържат SQL изрази в ключовете:

// Законосъобразно използване на оператори в ключовете
$table->where([
    'age > ?' => 18,
    'ROUND(score, ?) > ?' => [2, 75.5],
]);

// ❌ БЕЗОПАСНО: атакуващият може да инжектира свой собствен SQL
$_GET = ['1) UNION SELECT name, salary FROM users WHERE (is_admin = ?' => 1];
$table->where($_GET); // позволява на нападателя да получи администраторски заплати

Това отново е SQL инжекция.

Създаване на бял списък на колоните

Ако искате да разрешите на потребителите да избират колони, винаги използвайте бял списък:

// ✅ Безопасна обработка - само разрешени колони
$allowedColumns = ['name', 'email', 'active'];
$values = array_intersect_key($_GET, array_flip($allowedColumns));

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

Динамични идентификатори

За динамични имена на таблици и колони използвайте заместителя ?name:

// ✅ Безопасно използване на надеждни идентификатори
$table = 'users';
$column = 'name';
$database->query('SELECT ?name FROM ?name', $column, $table);

// ❌ UNSAFE - никога не използвайте потребителски вход
$database->query('SELECT ?name FROM users', $_GET['column']);

Символът ?name трябва да се използва само за доверени стойности, дефинирани в кода на приложението. За стойности, предоставени от потребителя, отново използвайте бял списък.