-
-
Notifications
You must be signed in to change notification settings - Fork 277
Expand file tree
/
Copy pathsecurity.texy
More file actions
185 lines (123 loc) · 8.7 KB
/
security.texy
File metadata and controls
185 lines (123 loc) · 8.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
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']);
```