Kaj je SQL-injekcija?
SQL-injekcije predstavljajo veliko grožnjo za relacijske baze podatkov in občutljive podatke, ki jih vsebujejo. Zato je celovita zaščita pred temi nepooblaščenimi poskusi zunanjega dostopa – ki jih omogočajo varnostne ranljivosti – nujno potrebna.
Kaj je SQL-injekcija?
SQL-injekcija je vrsta napada, ki izkorišča varnostno ranljivost v relacijskih podatkovnih sistemih, ki za obdelavo uporabniških vnosov uporabljajo jezik poizvedb SQL. Napadalec izkoristi uporabniške vnose, ki niso ustrezno zaščiteni in vsebujejo posebne znake, kot so dvojni vezaji, narekovaji ali podpičja. Ti znaki imajo posebne funkcije za SQL-interpreter in omogočajo zunanjo manipulacijo izvrševanih ukazov. SQL-injekcije so pogosto povezane z aplikacijami PHP in ASP, ki temeljijo na zastarelih vmesnikih. V mnogih od teh primerov vnos ni ustrezno očiščen, kar ga naredi za glavni cilj napada.
S strateškim vstavljanjem funkcionalnih znakov lahko nepooblaščeni uporabnik vnese dodatne ukaze SQL in manipulira 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 prevzamejo popoln nadzor nad strežnikom 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 najpogosteje uporabljanih tehnik med kiberkriminalci po vsem svetu. Napadalci uporabljajo različne strategije in izkoriščajo tako novo odkrite kot tudi že dolgo znane varnostne pomanjkljivosti v aplikacijah, vključenih v proces upravljanja s podatki. Da bi bolje razumeli, kako SQL-injekcija deluje v praksi, si kot primer oglejmo dve pogosti metodi napada.
Primer 1: Dostop prek neustrezno obdelanih uporabniških vnosov
Za dostop do baze podatkov se od uporabnikov običajno zahteva, da se najprej avtentificirajo. V ta namen se pogosto uporabljajo skripti, ki prikažejo prijavni obrazec z poljema za uporabniško ime in geslo. Uporabniki 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 aplikaciji bi ustrezne vrstice skripta za dostop do baze podatkov (z uporabo psevdokoda, 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)pythonNapadalec lahko zdaj z uporabo SQL-injekcije manipulira 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'"pythonTo napadalcu omogoča popoln dostop do celotne tabele uporabnikov 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 spreminjati tudi polje za uporabniško ime.
Primer 2: Izpisovanje podatkov z manipulacijo identifikatorjev
Iskanje podatkov v bazi podatkov po identifikatorju je praktična in pogosta metoda, vendar hkrati odpira možnost za SQL-injekcijo. Spletni strežnik na primer na podlagi identifikatorja, posredovanega 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);
}
?>phpPričakovani URL sledi vzorcu .../script.php?id=22. V tem primeru bi se prikazal vnos v tabeli z ID-jem »22«. Če bi nepooblaščena oseba imela možnost spreminjati ta URL in namesto tega poslala zahtevo, kot je .../script.php?id=22+OR+1=1, bi to povzročilo prikaz vseh vrstic v tabeli:
SELECT * FROM table WHERE id=22 OR 1=1;sqlKako kriminalci odkrijejo ranljive podatkovne baze?
Načeloma je vsaka spletna stran ali spletna aplikacija, ki uporablja podatkovne baze SQL brez pripravljenih poizvedb (prepared statements) ali drugih zaščitnih ukrepov, lahko izpostavljena napadom SQL-injekcij. Odkrite ranljivosti na svetovnem spletu ne ostanejo dolgo skrite. Obstajajo namreč spletne strani, ki objavljajo ažurne sezname znanih varnostnih pomanjkljivosti – in celo pojasnjujejo, kako lahko napadalci z iskanjem v Googlu poiščejo ustrezne spletne projekte. Če spletna stran vrne podrobna sporočila o napakah SQL, lahko kiberkriminalci ta sporočila uporabijo za prepoznavanje potencialnih ranljivosti. Na primer, že dodajanje apostrofa na konec URL-ja, ki vključuje parameter ID, lahko razkrije šibkost, kot je prikazano v naslednjem 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 pridobivanje podatkov o številu stolpcev, imenih tabel in stolpcev, različici jezika SQL ali celo uporabniških imenih in geslih. Poleg tega obstajajo različna orodja, ki lahko avtomatizirajo tako proces odkrivanja kot izvajanje napadov s SQL-injekcijo.
Kako zaščititi svojo bazo podatkov pred SQL-injekcijo
Obstajajo različne metode, ki jih lahko uporabite za preprečevanje napadov z vbrizgavanjem SQL v vaš sistem baz podatkov. Poskrbeti morate za vse vključene komponente – strežnik in posamezne aplikacije ter sistem za upravljanje baz podatkov.
Korak 1: Spremljajte avtomatske vnose iz aplikacij
Pri obdelavi vhodnih podatkov iz zunanjih ali vgrajenih aplikacij je nujno, da se poslane vrednosti preverijo in filtrirajo, da se prepreči SQL-injekcije.
1. Preverjanje tipov podatkov
Vsak vnos mora ustrezati pričakovanemu podatkovnemu tipu. Če je na primer zahtevan številčni vnos, bi preprosta preverjanje v PHP-ju lahko izgledalo takole:
if (filter_var($input, FILTER_VALIDATE_INT) === false) {
throw new InvalidArgumentException("Invalid input");
}phpPodobne preverjanja je treba izvajati tudi za nize znakov, datume ali druge posebne oblike.
2. Filtriranje posebnih znakov
Posebni znaki lahko povzročajo varnostne ranljivosti, 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. Izogibajte se prikazovanju sporočil o napakah
Izogibajte se neposrednim sporočilom o napakah, ki razkrivajo tehnične podrobnosti 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.");php4. Uporabite pripravljene izjave
Eden od najučinkovitejših načinov za preprečevanje SQL-vsiljivk je uporaba pripravljenih izjav. Pri tem pristopu se ukazi SQL in parametri pošiljajo ločeno, tako da se zlonameren 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();phpSistem za upravljanje podatkovnih baz samodejno poskrbi za varno obdelavo vnesenih podatkov.
Korak 2: Zagotovite celovito zaščito strežnika
Varnost strežnika, na katerem teče vaš sistem za upravljanje podatkovnih baz, prav tako igra ključno vlogo pri preprečevanju vbrizgavanja SQL. Ključni ukrep je okrepitev varnosti operacijskega sistema s pomočjo naslednjih priporočenih praks:
- Namestite ali omogočite le tiste aplikacije in storitve, ki so nujne za delovanje podatkovne zbirke.
- Odstranite vse neuporabljene ali nepotrebne uporabniške račune.
- Poskrbite, da so vse ustrezne posodobitve sistema in programske opreme takoj nameščene.
- Uporabite načelo najmanjših privilegijev, da uporabnikom in storitvam dodelite le najmanjša potrebna dovoljenja.
Glede na varnostne zahteve vašega spletnega projekta bi morali razmisliti o dodatnih zaščitnih ukrepih:
- Sistemi za zaznavanje vdorov (IDS) in sistemi za preprečevanje vdorov (IPS): Ti sistemi uporabljajo različne metode zaznavanja za zgodnje odkrivanje napadov, pošiljanje opozoril in – v primeru uporabe sistema IPS – samodejno sprožanje protiukrepov.
- Vrata na ravni aplikacij (ALG): ALG nadzoruje in filtrira promet med aplikacijami in spletnimi brskalniki neposredno na ravni aplikacij.
- Požarni zid za spletne aplikacije (WAF): WAF posebej ščiti spletne aplikacije pred vbrizgavanjem SQL in meddomenskim skriptiranjem (XSS) z blokiranjem ali čiščenjem sumljivih zahtevkov.
- Pristop Zero Trust: Ta sodoben varnostni model zagotavlja, da se vsak poskus dostopa – ne glede na njegov izvor – preveri in avtentificira, preden se dostop odobri.
- Pravila požarnega zidu in segmentacija omrežja: Ta sta bistvena za dolgoročno zmanjšanje površine za napade.
- Redni pregledi IT varnosti in testi penetracije: Ti pomagajo odkriti in odpraviti ranljivosti v zgodnji fazi.
Korak 3: Zaščitite bazo podatkov in uporabljajte varno kodo
Tako kot operacijski sistem mora biti tudi vaša baza podatkov osvobojena vseh nepotrebnih komponent in redno posodobljena. Odstranite vse shranjene postopke, ki jih ne potrebujete, ter onemogočite vse neuporabljane storitve in uporabniške račune. Ustvarite namenski račun za bazo podatkov, namenjen izključno spletnemu dostopu, in mu dodelite le najmanjša potrebna dovoljenja.
Glede na uporabo pripravljenih poizvedb se močno priporoča, da ne uporabljate modula mysql PHP (ki je bil v PHP 7 odstranjen). Namesto tega raje izberite mysqli ali PDO, da zagotovite večjo varnost in združljivost. 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();phpGesla se nikoli ne smejo shranjevati neposredno v bazi podatkov ali prikazovati v obliki navadnega besedila. Namesto tega uporabite metodo hashiranja, kot je password_hash(), v kombinaciji 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.";
}phpKaj imajo mize »bobby« opraviti s SQL-injekcijo?
Spletna stran bobby-tables.com s pomočjo spletnega stripa xkcd na duhovit način ponazarja nevarnosti, ki jih predstavljajo nezavarovani uporabniški vnosi v podatkovnih bazah. V stripu mama prejme telefonski klic iz šole svojega sina (ljubkovalno imenovanega Little Bobby Tables). Klicatelj jo vpraša, ali se njen sin res imenuje Robert'); DROP TABLE Students;--, na kar ona odgovori pritrdilno. 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 preverjala vnose v bazo podatkov.
Strip jasno prikazuje katastrofalne posledice, ki lahko nastanejo, če v aplikacijah za delo z bazami podatkov uporabniških vnosov ne preverimo in očistimo pravilno.