Varnostna tveganja

Podatkovne zbirke pogosto vsebujejo občutljive podatke in omogočajo izvajanje nevarnih operacij. Za varno delo s podatkovno zbirko Nette so ključni naslednji vidiki:

  • Razumevanje razlike med varnim in negotovim API
  • uporaba parametriziranih poizvedb
  • pravilno preverjanje vhodnih podatkov

Kaj je vdor SQL?

Vbrizgavanje SQL je najresnejše varnostno tveganje pri delu s podatkovnimi zbirkami. Pojavi se, ko nefiltriran uporabniški vnos postane del poizvedbe SQL. Napadalec lahko vstavi svoje ukaze SQL in s tem:

  • pridobi nepooblaščene podatke
  • spremeni ali izbriše podatke v zbirki podatkov
  • obide avtentikacijo
// ❌ NEVARNA KODA - občutljiva na vbrizgavanje SQL
$database->query("SELECT * FROM users WHERE name = '$_GET[name]'");

// Napadalec lahko vnese vrednost, kot je: ' ALI '1'='1
// Rezultat poizvedbe bi bil: SELECT * FROM users WHERE name = '' OR '1'='1'
// ki vrne vse uporabnike

Enako velja za Raziskovalca podatkovne zbirke:

// ❌ NEVARNA KODA - občutljiva na vbrizgavanje SQL
$table->where('name = ' . $_GET['name']);
$table->where("name = '$_GET[name]'");

Varne parametrirane poizvedbe

Temeljna zaščita pred vbrizgavanjem SQL so parametrirane poizvedbe. Podatkovna baza Nette ponuja več načinov za njihovo uporabo.

Najpreprostejši način je uporaba zahtevnih oznak:

// ✅ Varna parametrizirana poizvedba
$database->query('SELECT * FROM users WHERE name = ?', $name);

// ✅ Varen pogoj v Raziskovalcu
$table->where('name = ?', $name);

To velja za vse druge metode v Raziskovalcu podatkovne baze, ki omogočajo vstavljanje izrazov z nadomestnimi znaki z vprašajem in parametri.

Za stavke INSERT, UPDATE ali WHERE lahko vrednosti posredujete v polju:

// ✅ Varno vstavljanje
$database->query('INSERT INTO users', [
	'name' => $name,
	'email' => $email,
]);

// ✅ Varno vnašanje v Raziskovalcu
$table->insert([
	'name' => $name,
	'email' => $email,
]);

Potrditev vrednosti parametra

Parametrične poizvedbe so temelj varnega dela z zbirko podatkov. Vendar morajo vrednosti, ki jih posredujejo, prestati več stopenj preverjanja:

Preverjanje tipa

Zagotavljanje pravilne podatkovne vrste parametrov je ključnega pomena – to je nujen pogoj za varno uporabo podatkovne zbirke Nette. Podatkovna baza predpostavlja, da imajo vsi vhodni podatki pravilen podatkovni tip, ki ustreza stolpcu.

Če bi na primer $name v prejšnjih primerih nepričakovano postal polje namesto niza, bi podatkovna baza Nette Database poskusila vstaviti vse njegove elemente v poizvedbo SQL, kar bi povzročilo napako. Zato nepotrjenih podatkov iz $_GET, $_POST ali $_COOKIE nikoli ne uporabljajte neposredno v poizvedbah podatkovne zbirke.

Potrjevanje formata

Druga raven preverja obliko podatkov – na primer, ali so nizi kodirani v UTF-8 in ali se njihova dolžina ujema z definicijo stolpca ali ali ali so številčne vrednosti v dovoljenem območju za podatkovno vrsto stolpca.

Na tej ravni se lahko delno zanesete na samo podatkovno zbirko – mnoge podatkovne zbirke zavračajo neveljavne podatke. Vendar pa se lahko obnašajo različno: nekatere lahko nemo skrajšajo dolge nize ali izrežejo številke, ki so zunaj razpona.

Potrjevanje za posamezno domeno

Tretja raven vključuje logična preverjanja, specifična za vašo aplikacijo. Na primer preverjanje, ali se vrednosti iz izbirnih polj ujemajo z razpoložljivimi možnostmi, ali številke spadajo v pričakovano območje (npr. starost 0–150 let) ali ali ali so razmerja med vrednostmi smiselna.

  • Uporabite obrazce Nette, ki samodejno poskrbijo za pravilno preverjanje vseh vnosov.
  • Uporabite predstavnike in prijavite podatkovne vrste parametrov v metodah action*() in render*().
  • Ali pa implementirajte lasten nivo potrjevanja z uporabo standardnih orodij PHP, kot je filter_var().

Varno delo s stolpci

V prejšnjem razdelku smo opisali, kako pravilno potrditi vrednosti parametrov. Pri uporabi matrik v poizvedbah SQL pa je treba enako pozornost nameniti tudi njihovim ključem.

// ❌ NEVARNA KODA - ključi polj niso sanitizirani
$database->query('INSERT INTO users', $_POST);

Pri ukazih INSERT in UPDATE je to velika varnostna pomanjkljivost – napadalec lahko vstavi ali spremeni kateri koli stolpec v zbirki podatkov. Tako lahko na primer nastavi is_admin = 1 ali vstavi poljubne podatke v občutljive stolpce (tako imenovana ranljivost množičnega dodeljevanja).

Pri pogojih WHERE je to še bolj nevarno, saj lahko vsebujejo operatorje:

// ❌ NEVARNA KODA - ključi polj niso sanitizirani
$_POST['salary >'] = 100000;
$database->query('SELECT * FROM users WHERE', $_POST);
// izvede poizvedbo WHERE (`salary` > 100000)

Napadalec lahko ta pristop uporabi za sistematično odkrivanje plač zaposlenih. Začne lahko s poizvedbo po plačah nad 100.000, nato pod 50.000 in s postopnim zmanjševanjem razpona razkrije približne plače vseh zaposlenih. Ta vrsta napada se imenuje naštevanje SQL.

Metodi where() in whereOr() sta še bolj prilagodljivi in podpirata izraze SQL, vključno z operaterji in funkcijami, tako v ključih kot v vrednostih. Tako lahko napadalec izvede zapleteno vbrizgavanje SQL:

// ❌ Nevarna koda - napadalec lahko vstavi svoj SQL
$_POST = ['0) UNION SELECT name, salary FROM users WHERE (1'];
$table->where($_POST);
// izvede poizvedbo WHERE (0) UNION SELECT ime, plača FROM uporabniki WHERE (1)

Ta napad zaključi prvotni pogoj s 0), doda svoj SELECT z uporabo UNION za pridobitev občutljivih podatkov iz tabele users in zaključi s sintaktično pravilno poizvedbo z uporabo WHERE (1).

Bela lista stolpcev

Za varno delo z imeni stolpcev potrebujete mehanizem, ki zagotavlja, da lahko uporabniki delujejo samo z dovoljenimi stolpci in ne morejo dodajati lastnih. Poskus odkrivanja in blokiranja nevarnih imen stolpcev (črni seznam) je nezanesljiv – napadalec si lahko vedno izmisli nov način zapisa nevarnega imena stolpca, ki ga niste predvideli.

Zato je veliko varneje obrniti logiko in določiti izrecni seznam dovoljenih stolpcev (bela lista):

// Stolpci, ki jih lahko uporabnik spreminja
$allowedColumns = ['name', 'email', 'active'];

// Odstranitev vseh nepooblaščenih stolpcev iz vnosa
$filteredData = array_intersect_key($userData, array_flip($allowedColumns));

// ✅ Zdaj je varno uporabljati v poizvedbah, kot so:
$database->query('INSERT INTO users', $filteredData);
$table->update($filteredData);
$table->where($filteredData);

Dinamični identifikatorji

Za dinamična imena tabel in stolpcev uporabite nosilec ?name. S tem zagotovite pravilno eskapiranje identifikatorjev v skladu z določeno sintakso podatkovne zbirke (npr. uporaba zaklepajev v MySQL):

// ✅ Varna uporaba zaupanja vrednih identifikatorjev
$table = 'users';
$column = 'name';
$database->query('SELECT ?name FROM ?name', $column, $table);
// Rezultat v MySQL: SELECT `name` FROM `users`

Pomembno: Simbol ?name uporabljajte samo za zaupanja vredne vrednosti, opredeljene v aplikacijski kodi. Za vrednosti, ki jih zagotovi uporabnik, ponovno uporabite beli seznam. V nasprotnem primeru tvegate varnostne ranljivosti:

// ❌ Nevarno - nikoli ne uporabljajte uporabniškega vnosa
$database->query('SELECT ?name FROM users', $_GET['column']);
različica: 4.0