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ă.

versiune: 4.0