-
-
Notifications
You must be signed in to change notification settings - Fork 281
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
49 changed files
with
12,795 additions
and
4,396 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
Large diffs are not rendered by default.
Oops, something went wrong.
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,145 @@ | ||
**Риск за сигурността** | ||
*********************** | ||
|
||
.[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` трябва да се използва само за доверени стойности, дефинирани в кода на приложението. За стойности, предоставени от потребителя, отново използвайте бял списък. |
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
Oops, something went wrong.