Riscuri de securitate
Bazele de date conțin adesea date sensibile și permit operațiuni periculoase. Nette Database oferă o serie de caracteristici de securitate. Cu toate acestea, este esențial să înțelegeți diferența dintre API-urile sigure și cele nesigure.
Injecție SQL
Injecția SQL este cel mai grav risc de securitate atunci când se lucrează cu baze de date. Aceasta are loc atunci când o intrare necontrolată a utilizatorului devine parte a unei interogări SQL. Un atacator își poate injecta propriile comenzi SQL, obținând sau modificând date din baza de date.
// ❌ UNSAFE CODE - vulnerabil la injectarea SQL
$database->query("SELECT * FROM users WHERE name = '$_GET[name]'");
// Atacatorul poate introduce ceva de genul: ' OR '1'='1
// Interogarea rezultată va fi:
// SELECT * FROM users WHERE name = '' OR '1'='1'
// Aceasta returnează toți utilizatorii!
Același lucru este valabil și pentru Database Explorer:
// ❌ CODUL UNSAFE
$table->where('name = ' . $_GET['name']);
$table->where("name = '$_GET[name]'");
Interogări parametrizate sigure
Modul sigur de a introduce valori în interogările SQL este prin interogări parametrizate. Nette Database oferă mai multe modalități de utilizare a acestora.
Semne de întrebare de tip Placeholder
Cea mai simplă metodă este utilizarea semnelor de întrebare:
// ✅ Interogări parametrizate sigure
$database->query('SELECT * FROM users WHERE name = ?', $_GET['name']);
// ✅ Condiție sigură în Explorer
$table->where('name = ?', $_GET['name']);
Același lucru este valabil pentru toate celelalte metode din Database Explorer care permit inserarea de expresii cu semne de întrebare și parametri.
Valorile trebuie să fie de tip scalar (string
, int
, float
,
bool
) sau null
. Dacă, de exemplu, $_GET['name']
este un array, Nette Database va include
toate elementele sale în interogarea SQL, ceea ce poate fi nedorit.
Array-uri de valori
Pentru clauzele INSERT
, UPDATE
, sau WHERE
, putem utiliza matrice de valori:
// ✅ INSERTARE sigură
$database->query('INSERT INTO users', [
'name' => $_GET['name'],
'email' => $_GET['email'],
]);
// ✅ UPDATE sigur
$database->query('UPDATE users SET', [
'name' => $_GET['name'],
'email' => $_GET['email'],
], 'WHERE id = ?', $_GET['id']);
Nette Database evadează automat toate valorile trecute prin interogări parametrizate. Cu toate acestea, trebuie să ne asigurăm de tipul corect de date al parametrilor.
Cheile array nu sunt un API sigur
În timp ce valorile din array-uri sunt sigure, nu același lucru se poate spune despre chei:
// ❌ UNSAFE CODE - cheile pot conține o injecție SQL
$database->query('INSERT INTO users', $_GET);
$database->query('SELECT * FROM users WHERE', $_GET);
$table->where($_GET);
Pentru comenzile INSERT
și UPDATE
, acesta este un defect de securitate critic – un atacator ar
putea introduce sau modifica orice coloană din baza de date. De exemplu, ar putea seta is_admin = 1
sau introduce
date arbitrare în coloane sensibile.
În condițiile WHERE
, acest lucru este și mai periculos, deoarece permite SQL enumeration –
o tehnică de a extrage treptat informații despre baza de date. Un atacator ar putea încerca să exploreze salariile
angajaților injectând în $_GET
astfel:
$_GET = ['salary >', 100000]; // începe să stabilească intervalele salariale
Cu toate acestea, principala problemă este că condițiile WHERE
acceptă expresii SQL în chei:
// Utilizarea legitimă a operatorilor în chei
$table->where([
'age > ?' => 18,
'ROUND(score, ?) > ?' => [2, 75.5],
]);
// ❌ INSEGURĂ: atacatorul își poate injecta propriul SQL
$_GET = ['1) UNION SELECT name, salary FROM users WHERE (is_admin = ?' => 1];
$table->where($_GET); // permite atacatorului să obțină salariile administratorilor
Aceasta este încă o dată injecție SQL.
Lista albă a coloanelor
Dacă doriți să permiteți utilizatorilor să aleagă coloanele, utilizați întotdeauna o listă albă:
// ✅ Procesare sigură - numai coloane permise
$allowedColumns = ['name', 'email', 'active'];
$values = array_intersect_key($_GET, array_flip($allowedColumns));
$database->query('INSERT INTO users', $values);
Identificatori dinamici
Pentru numele dinamice ale tabelelor și coloanelor, utilizați marcajul ?name
:
// ✅ Utilizarea în siguranță a identificatorilor de încredere
$table = 'users';
$column = 'name';
$database->query('SELECT ?name FROM ?name', $column, $table);
// ❌ UNSAFE - nu utilizați niciodată date introduse de utilizator
$database->query('SELECT ?name FROM users', $_GET['column']);
Simbolul ?name
ar trebui utilizat numai pentru valorile de încredere definite în codul aplicației. Pentru
valorile furnizate de utilizator, utilizați din nou o listă albă.