SQL-injekcije pred­sta­vlja­jo veliko grožnjo za re­la­cij­ske baze podatkov in ob­ču­tlji­ve podatke, ki jih vsebujejo. Zato je celovita zaščita pred temi ne­po­o­bla­šče­ni­mi poskusi zunanjega dostopa – ki jih omogočajo varnostne ran­lji­vo­sti – nujno potrebna.

Kaj je SQL-injekcija?

SQL-injekcija je vrsta napada, ki izkorišča varnostno ran­lji­vost v re­la­cij­skih po­dat­kov­nih sistemih, ki za obdelavo upo­rab­ni­ških vnosov upo­ra­blja­jo jezik poizvedb SQL. Napadalec izkoristi upo­rab­ni­ške vnose, ki niso ustrezno zaščiteni in vsebujejo posebne znake, kot so dvojni vezaji, na­re­ko­va­ji ali podpičja. Ti znaki imajo posebne funkcije za SQL-in­ter­pre­ter in omogočajo zunanjo ma­ni­pu­la­ci­jo iz­vr­še­va­nih ukazov. SQL-injekcije so pogosto povezane z apli­ka­ci­ja­mi PHP in ASP, ki temeljijo na za­sta­re­lih vmesnikih. V mnogih od teh primerov vnos ni ustrezno očiščen, kar ga naredi za glavni cilj napada.

S stra­te­škim vsta­vlja­njem funk­ci­o­nal­nih znakov lahko ne­po­o­bla­šče­ni uporabnik vnese dodatne ukaze SQL in ma­ni­pu­li­ra z vnosi v bazi podatkov, da bi prebral, spremenil ali izbrisal podatke. V hujših primerih lahko napadalci pridobijo celo dostop do ukazne vrstice sistema, kar jim lahko omogoči, da pre­vza­me­jo popoln nadzor nad stre­žni­kom baze podatkov.

Primer SQL-injekcije, ki prikazuje, kako poteka napad na bazo podatkov

Ker je mogoče ranljive strežnike z bazami podatkov hitro odkriti in ker so napadi s SQL-injekcijo relativno enostavni za izvedbo, ta metoda ostaja ena naj­po­go­ste­je upo­ra­blja­nih tehnik med ki­ber­kri­mi­nal­ci po vsem svetu. Napadalci upo­ra­blja­jo različne stra­te­gi­je in iz­ko­ri­šča­jo tako novo odkrite kot tudi že dolgo znane varnostne po­manj­klji­vo­sti v apli­ka­ci­jah, vklju­če­nih v proces upra­vlja­nja s podatki. Da bi bolje razumeli, kako SQL-injekcija deluje v praksi, si kot primer oglejmo dve pogosti metodi napada.

Primer 1: Dostop prek ne­u­stre­zno obdelanih upo­rab­ni­ških vnosov

Za dostop do baze podatkov se od upo­rab­ni­kov običajno zahteva, da se najprej av­ten­ti­fi­ci­ra­jo. V ta namen se pogosto upo­ra­blja­jo skripti, ki prikažejo prijavni obrazec z poljema za upo­rab­ni­ško ime in geslo. Upo­rab­ni­ki izpolnijo obrazec, skript pa nato preveri, ali v bazi podatkov obstajajo ustrezni vnosi. Privzeto lahko baza podatkov vsebuje tabelo z imenom users s stolpci username in password. V tipični spletni apli­ka­ci­ji bi ustrezne vrstice skripta za dostop do baze podatkov (z uporabo psev­do­ko­da, podobnega Pythonu) lahko izgledale takole:

uname = request.POST['username']
passwd = request.POST['password']
sql = "SELECT id FROM users WHERE username='" + uname + "' AND password='" + passwd + "'"
database.execute(sql)
python

Napadalec lahko zdaj z uporabo SQL-injekcije ma­ni­pu­li­ra s poljem za geslo, na primer tako, da vnese password' OR 1='1, kar povzroči naslednje SQL-poizvedbo:

sql = "SELECT id FROM users WHERE username='' AND password='password' OR 1='1'"
python

To napadalcu omogoča popoln dostop do celotne tabele upo­rab­ni­kov v bazi podatkov, saj se pogoj za geslo vedno izkaže za resničen (1='1'). Če se napadalec prijavi kot skrbnik, lahko prosto spreminja kateri koli vnos v bazi podatkov. Na enak način je mogoče spre­mi­nja­ti tudi polje za upo­rab­ni­ško ime.

Primer 2: Iz­pi­so­va­nje podatkov z ma­ni­pu­la­ci­jo iden­ti­fi­ka­tor­jev

Iskanje podatkov v bazi podatkov po iden­ti­fi­ka­tor­ju je praktična in pogosta metoda, vendar hkrati odpira možnost za SQL-injekcijo. Spletni strežnik na primer na podlagi iden­ti­fi­ka­tor­ja, po­sre­do­va­ne­ga v URL-ju, ve, katere podatke mora pridobiti iz baze podatkov. Ustrezni PHP-skript je videti takole:

<?php
    $mysqli = new mysqli("localhost", "username", "password", "database");
    $id = intval($_GET['id']);
    $result = $mysqli->query("SELECT * FROM table WHERE id=$id");
    while ($row = $result->fetch_assoc()) {
        echo print_r($row, true);
    }
?>
php

Pri­ča­ko­va­ni URL sledi vzorcu .../script.php?id=22. V tem primeru bi se prikazal vnos v tabeli z ID-jem »22«. Če bi ne­po­o­bla­šče­na oseba imela možnost spre­mi­nja­ti ta URL in namesto tega poslala zahtevo, kot je .../script.php?id=22+OR+1=1, bi to pov­zro­či­lo prikaz vseh vrstic v tabeli:

SELECT * FROM table WHERE id=22 OR 1=1;
sql

Kako kri­mi­nal­ci odkrijejo ranljive po­dat­kov­ne baze?

Načeloma je vsaka spletna stran ali spletna apli­ka­ci­ja, ki uporablja po­dat­kov­ne baze SQL brez pri­pra­vlje­nih poizvedb (prepared sta­te­ments) ali drugih zaščitnih ukrepov, lahko iz­po­sta­vlje­na napadom SQL-injekcij. Odkrite ran­lji­vo­sti na svetovnem spletu ne ostanejo dolgo skrite. Obstajajo namreč spletne strani, ki ob­ja­vlja­jo ažurne sezname znanih var­no­stnih po­manj­klji­vo­sti – in celo po­ja­snju­je­jo, kako lahko napadalci z iskanjem v Googlu poiščejo ustrezne spletne projekte. Če spletna stran vrne podrobna sporočila o napakah SQL, lahko ki­ber­kri­mi­nal­ci ta sporočila uporabijo za pre­po­zna­va­nje po­ten­ci­al­nih ran­lji­vo­sti. Na primer, že dodajanje apostrofa na konec URL-ja, ki vključuje parameter ID, lahko razkrije šibkost, kot je prikazano v na­sle­dnjem primeru:

[DomainName].com/news.php?id=5’

Ranljiva spletna stran vrne sporočilo o napaki v naslednji obliki:

Query failed: You have an error in your SQL syntax…

Podobne metode se lahko uporabijo tudi za pri­do­bi­va­nje podatkov o številu stolpcev, imenih tabel in stolpcev, različici jezika SQL ali celo upo­rab­ni­ških imenih in geslih. Poleg tega obstajajo različna orodja, ki lahko av­to­ma­ti­zi­ra­jo tako proces od­kri­va­nja kot izvajanje napadov s SQL-injekcijo.

Kako zaščititi svojo bazo podatkov pred SQL-injekcijo

Obstajajo različne metode, ki jih lahko uporabite za pre­pre­če­va­nje napadov z vbri­zga­va­njem SQL v vaš sistem baz podatkov. Poskrbeti morate za vse vključene kom­po­nen­te – strežnik in posamezne apli­ka­ci­je ter sistem za upra­vlja­nje baz podatkov.

Korak 1: Spre­mljaj­te av­to­mat­ske vnose iz aplikacij

Pri obdelavi vhodnih podatkov iz zunanjih ali vgrajenih aplikacij je nujno, da se poslane vrednosti preverijo in fil­tri­ra­jo, da se prepreči SQL-injekcije.

1. Pre­ver­ja­nje tipov podatkov

Vsak vnos mora ustrezati pri­ča­ko­va­ne­mu po­dat­kov­ne­mu tipu. Če je na primer zahtevan številčni vnos, bi preprosta pre­ver­ja­nje v PHP-ju lahko izgledalo takole:

if (filter_var($input, FILTER_VALIDATE_INT) === false) {
    throw new InvalidArgumentException("Invalid input");
}
php

Podobne pre­ver­ja­nja je treba izvajati tudi za nize znakov, datume ali druge posebne oblike.

2. Fil­tri­ra­nje posebnih znakov

Posebni znaki lahko pov­zro­ča­jo varnostne ran­lji­vo­sti, zlasti v okviru jezikov SQL ali HTML. Varna rešitev je uporaba vrednosti htmlspecialchars() za vnos HTML-ja in vrednosti PDO::quote() za poizvedbe SQL.

3. Iz­o­gi­baj­te se pri­ka­zo­va­nju sporočil o napakah

Iz­o­gi­baj­te se ne­po­sre­dnim spo­ro­či­lom o napakah, ki raz­kri­va­jo tehnične po­drob­no­sti o bazi podatkov ali sistemu. Namesto tega prikažite splošno sporočilo, na primer:

echo "An error occurred. Please try again later.";
error_log("Unexpected error encountered. See system log for details.");
php

4. Uporabite pri­pra­vlje­ne izjave

Eden od naj­u­čin­ko­vi­tej­ših načinov za pre­pre­če­va­nje SQL-vsiljivk je uporaba pri­pra­vlje­nih izjav. Pri tem pristopu se ukazi SQL in parametri pošiljajo ločeno, tako da se zlo­na­me­ren kod ne more izvršiti. Tukaj je primer izvedbe v PHP z uporabo PDO (PHP Data Objects):

$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->bindParam(':id', $user_id, PDO::PARAM_INT);
$stmt->execute();
php

Sistem za upra­vlja­nje po­dat­kov­nih baz samodejno poskrbi za varno obdelavo vnesenih podatkov.

Korak 2: Za­go­to­vi­te celovito zaščito strežnika

Varnost strežnika, na katerem teče vaš sistem za upra­vlja­nje po­dat­kov­nih baz, prav tako igra ključno vlogo pri pre­pre­če­va­nju vbri­zga­va­nja SQL. Ključni ukrep je okrepitev varnosti ope­ra­cij­ske­ga sistema s pomočjo na­sle­dnjih pri­po­ro­če­nih praks:

  • Namestite ali omogočite le tiste apli­ka­ci­je in storitve, ki so nujne za delovanje po­dat­kov­ne zbirke.
  • Od­stra­ni­te vse ne­u­po­ra­blje­ne ali ne­po­treb­ne upo­rab­ni­ške račune.
  • Poskrbite, da so vse ustrezne po­so­do­bi­tve sistema in pro­gram­ske opreme takoj nameščene.
  • Uporabite načelo naj­manj­ših pri­vi­le­gi­jev, da upo­rab­ni­kom in storitvam dodelite le najmanjša potrebna do­vo­lje­nja.

Glede na varnostne zahteve vašega spletnega projekta bi morali raz­mi­sli­ti o dodatnih zaščitnih ukrepih:

  • Sistemi za za­zna­va­nje vdorov (IDS) in sistemi za pre­pre­če­va­nje vdorov (IPS): Ti sistemi upo­ra­blja­jo različne metode za­zna­va­nja za zgodnje od­kri­va­nje napadov, po­ši­lja­nje opozoril in – v primeru uporabe sistema IPS – samodejno sprožanje pro­ti­u­kre­pov.
  • Vrata na ravni aplikacij (ALG): ALG nadzoruje in filtrira promet med apli­ka­ci­ja­mi in spletnimi br­skal­ni­ki ne­po­sre­dno na ravni aplikacij.
  • Požarni zid za spletne apli­ka­ci­je (WAF): WAF posebej ščiti spletne apli­ka­ci­je pred vbri­zga­va­njem SQL in med­do­men­skim skrip­ti­ra­njem (XSS) z blo­ki­ra­njem ali čiščenjem sumljivih zahtevkov.
  • Pristop Zero Trust: Ta sodoben varnostni model za­go­ta­vlja, da se vsak poskus dostopa – ne glede na njegov izvor – preveri in av­ten­ti­fi­ci­ra, preden se dostop odobri.
  • Pravila požarnega zidu in se­gmen­ta­ci­ja omrežja: Ta sta bistvena za dol­go­roč­no zmanj­ša­nje površine za napade.
  • Redni pregledi IT varnosti in testi pe­ne­tra­ci­je: Ti pomagajo odkriti in odpraviti ran­lji­vo­sti v zgodnji fazi.

Korak 3: Zaščitite bazo podatkov in upo­ra­bljaj­te varno kodo

Tako kot ope­ra­cij­ski sistem mora biti tudi vaša baza podatkov osvo­bo­je­na vseh ne­po­treb­nih komponent in redno po­so­do­blje­na. Od­stra­ni­te vse shranjene postopke, ki jih ne po­tre­bu­je­te, ter one­mo­go­či­te vse ne­u­po­ra­blja­ne storitve in upo­rab­ni­ške račune. Ustvarite namenski račun za bazo podatkov, namenjen izključno spletnemu dostopu, in mu dodelite le najmanjša potrebna do­vo­lje­nja.

Glede na uporabo pri­pra­vlje­nih poizvedb se močno priporoča, da ne upo­ra­blja­te modula mysql PHP (ki je bil v PHP 7 od­stra­njen). Namesto tega raje izberite mysqli ali PDO, da za­go­to­vi­te večjo varnost in zdru­žlji­vost. Varna poizvedba mysqli bi lahko izgledala takole:

$mysqli = new mysqli("localhost", "username", "password", "database");
if ($mysqli->connect_error) die("Connection failed");
$stmt = $mysqli->prepare("SELECT password FROM users WHERE username = ?");
$stmt->bind_param("s", $_POST['username']);
$stmt->execute();
$stmt->bind_result($hashedPassword);
if ($stmt->fetch() && password_verify($_POST['password'], $hashedPassword)) {
    echo "Login successful";
} else {
    echo "Invalid login credentials";
}
$stmt->close();
$mysqli->close();
php

Gesla se nikoli ne smejo shra­nje­va­ti ne­po­sre­dno v bazi podatkov ali pri­ka­zo­va­ti v obliki navadnega besedila. Namesto tega uporabite metodo ha­shi­ra­nja, kot je password_hash(), v kom­bi­na­ci­ji s password_verify(), da varno zaščitite prijavne podatke. Varna izvedba bi lahko izgledala takole:

$mysqli = new mysqli("localhost", "username", "password", "database");
$stmt = $mysqli->prepare("SELECT password FROM users WHERE username = ?");
$stmt->bind_param("s", $_POST['username']);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
if ($row && password_verify($_POST['password'], $row['password'])) {
    echo "Login successful!";
} else {
    echo "Incorrect username or password.";
}
php

Kaj imajo mize »bobby« opraviti s SQL-injekcijo?

Spletna stran bobby-tables.com s pomočjo spletnega stripa xkcd na duhovit način ponazarja ne­var­no­sti, ki jih pred­sta­vlja­jo ne­za­va­ro­va­ni upo­rab­ni­ški vnosi v po­dat­kov­nih bazah. V stripu mama prejme te­le­fon­ski klic iz šole svojega sina (ljub­ko­val­no ime­no­va­ne­ga Little Bobby Tables). Klicatelj jo vpraša, ali se njen sin res imenuje Robert'); DROP TABLE Students;--, na kar ona odgovori pri­tr­dil­no. Razlog za klic kmalu postane jasen: poskus vnosa imena Robert v bazo podatkov učencev je povzročil izbris celotne tabele učencev. Mati ni preveč sočutna – upa le, da se je šola iz tega naučila lekcijo in bo v prihodnje pre­ver­ja­la vnose v bazo podatkov.

Strip jasno prikazuje ka­ta­stro­fal­ne posledice, ki lahko nastanejo, če v apli­ka­ci­jah za delo z bazami podatkov upo­rab­ni­ških vnosov ne preverimo in očistimo pravilno.

Go to Main Menu