SQL įterpimai kelia didelę grėsmę re­lia­ci­nėms duomenų bazėms ir jose saugomai kon­fi­den­cia­liai in­for­ma­ci­jai. Būtent todėl būtina už­tik­rin­ti vi­sa­pu­siš­ką apsaugą nuo šių neteisėtų išorinių prieigos bandymų, kuriuos leidžia saugumo pa­žei­džia­mu­mai.

Kas yra SQL įterpimas?

SQL įterpimas – tai atakos rūšis, kuria pa­si­nau­do­ja­ma siekiant išnaudoti saugumo spragą re­lia­ci­nė­se duomenų bazių sistemose, kurios naudoja SQL užklausų kalbą vartotojo įvestims apdoroti. Puolėjas pa­si­nau­do­ja vartotojo įvestimis, kurios nėra tinkamai ap­sau­go­tos ir kuriose yra specialių simbolių, pvz., dvigubų brūkšne­lių, kabučių ar kab­lia­taš­kių. Šie simboliai turi spe­cia­lias funkcijas SQL in­ter­preta­to­riui ir leidžia išoriškai ma­ni­pu­liuo­ti vyk­do­mo­mis ko­man­do­mis. SQL in­jek­ci­jos dažnai siejamos su PHP ir ASP prog­ra­mo­mis, kurios naudoja pa­se­nu­sias sąsajas. Daugeliu šių atvejų įvestis nėra tinkamai išvalyta, todėl tampa pag­rin­di­niu atakos taikiniu.

Stra­te­giš­kai įterp­da­mas funk­ci­nius simbolius, ne­tei­sė­tas var­to­to­jas gali įterpti pa­pil­do­mas SQL komandas ir ma­ni­pu­liuo­ti duomenų bazės įrašais, kad galėtų skaityti, keisti ar trinti duomenis. Sun­kiau­siais atvejais už­puo­li­kai gali netgi gauti prieigą prie sistemos komandų eilutės, o tai jiems gali leisti visiškai perimti duomenų bazės serverio kontrolę.

SQL įterpimo pavyzdys, iliust­ruo­jan­tis, kaip vyksta duomenų bazės ataka

Kadangi pa­žei­džia­mus duomenų bazių serverius galima greitai nustatyti, o SQL įterpimo atakas vykdyti palyginti lengva, šis metodas vis dar yra vienas iš pla­čiau­siai naudojamų būdų tarp ki­ber­ne­ti­nių nu­si­kal­tė­lių visame pasaulyje. Pik­ta­da­riai taiko įvairias stra­te­gi­jas, pa­si­nau­do­da­mi tiek naujai at­ras­to­mis, tiek seniai žinomomis saugumo spragomis prog­ra­mo­se, nau­do­ja­mo­se duomenų tvarkymo procese. Norėdami geriau suprasti, kaip SQL įterpimas veikia prak­ti­ko­je, pa­nag­ri­nė­ki­me du daž­niau­siai pa­si­tai­kan­čius atakos metodus kaip pa­vyz­džius.

1 pavyzdys: prieiga per ne­tin­ka­mai apdorotą vartotojo įvestį

Norėdami pri­si­jung­ti prie duomenų bazės, var­to­to­jai paprastai turi pir­miau­sia pa­tvir­tin­ti savo tapatybę. Šiam tikslui daž­niau­siai naudojami skriptai, kurie rodo pri­si­jun­gi­mo formą su lau­ke­liais vartotojo vardui ir slap­ta­žo­džiui įvesti. Var­to­to­jai užpildo formą, o skriptas patikrina, ar duomenų bazėje yra ati­tin­ka­mų įrašų. Pagal nu­ma­ty­tuo­sius nu­sta­ty­mus duomenų bazėje gali būti lentelė pa­va­di­ni­mu users su stul­pe­liais username ir password. Tipinėje ži­nia­tink­lio prog­ra­mo­je ati­tin­ka­mos skripto eilutės, skirtos pri­si­jun­gi­mui prie duomenų bazės (naudojant Python tipo pse­udo­ko­dą), gali atrodyti taip:

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

Dabar įsi­lau­žė­lis gali ma­ni­pu­liuo­ti slap­ta­žo­džio laukeliu nau­do­da­mas SQL įterpimą, pa­vyz­džiui, įvedęs password' OR 1='1, dėl ko susidaro tokia SQL užklausa:

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

Tai suteikia įsi­lau­žė­liui visišką prieigą prie visos vartotojų lentelės duomenų bazėje, nes slap­ta­žo­džio sąlyga visada įver­ti­na­ma kaip teisinga (1='1'). Jei įsi­lau­žė­lis pri­si­jungs kaip ad­mi­nist­ra­to­rius, jis galės laisvai keisti bet kurį duomenų bazės įrašą. Be to, vartotojo vardo lauką galima ma­ni­pu­liuo­ti tokiu pačiu būdu.

2 pavyzdys: Duomenų išgava ma­ni­pu­liuo­jant iden­ti­fi­ka­to­riais

In­for­ma­ci­jos paieška duomenų bazėje pagal ID yra prak­tiš­kas ir plačiai nau­do­ja­mas metodas, tačiau kartu sudaro galimybes SQL įterpimui. Pa­vyz­džiui, ži­nia­tink­lio serveris pagal URL adrese perduotą ID žino, kokią in­for­ma­ci­ją turi iškviesti iš duomenų bazės. Ati­tin­ka­mas PHP skriptas atrodo taip:

<?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

Tikėtinas URL atitinka .../script.php?id=22 šabloną. Šiuo atveju būtų iškeltas lentelės įrašas su ID „22“. Jei ne­įga­lio­tas asmuo turėtų galimybę pakeisti šį URL ir vietoj to išsiųstų užklausą, pa­vyz­džiui, kaip .../script.php?id=22+OR+1=1, dėl to būtų iškeltos visos lentelės eilutės:

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

Kaip nu­si­kal­tė­liai suranda pa­žei­džia­mas duomenų bazių sistemas?

Iš esmės bet kuri svetainė ar in­ter­ne­ti­nė programa, kurioje nau­do­ja­mos SQL duomenų bazės be iš anksto paruoštų užklausų (prepared sta­te­ments) ar kitų apsaugos priemonių, gali būti pa­žei­džia­ma SQL įterpimo atakoms. Atrastos pa­žei­džia­my­bės pa­sau­li­nia­me tinkle ilgai paslėptos nelieka. Iš tiesų yra svetainių, kurios skelbia nau­jau­sius žinomų saugumo spragų sąrašus ir net paaiškina, kaip įsi­lau­žė­liai gali pa­si­nau­do­ti „Google“ paieška, kad surastų ati­tin­kan­čius in­ter­ne­ti­nius projektus. Jei svetainė pateikia išsamius SQL klaidų pra­ne­ši­mus, ki­ber­ne­ti­niai nu­si­kal­tė­liai gali juos panaudoti po­ten­cia­lioms pa­žei­džia­moms vietoms nustatyti. Pa­vyz­džiui, pridėjus apostrofą prie URL, kuriame yra ID pa­ra­met­ras, gali būti at­skleis­ta silpnoji vieta, kaip parodyta šiame pavyzdyje:

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

Pa­žei­džia­ma svetainė grąžina klaidos pranešimą tokia forma:

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

Panašūs metodai taip pat gali būti naudojami stulpelių skaičiui, lentelių ir stulpelių pa­va­di­ni­mams, SQL versijai ar net vartotojų vardams ir slap­ta­žo­džiams išgauti. Be to, yra įvairių įrankių, kurie gali au­to­ma­ti­zuo­ti tiek paieškos procesą, tiek SQL įterpimo atakų vykdymą.

Kaip apsaugoti duomenų bazę nuo SQL įterpimo

Yra įvairių metodų, kuriuos galite taikyti, siekdami užkirsti kelią SQL įterpimo atakoms prieš jūsų duomenų bazių sistemą. Tu­rė­tu­mė­te at­si­žvelg­ti į visus su­si­ju­sius kom­po­nen­tus – serverį, atskiras programas ir duomenų bazių valdymo sistemą.

1 žingsnis: stebėti au­to­ma­ti­nius duomenų įvedimus iš programų

Ap­do­ro­jant iš išorinių ar įterptųjų programų gaunamus duomenis, būtina pa­tik­rin­ti ir filtruoti pateiktas reikšmes, kad būtų išvengta SQL įterpimų.

1. Pa­tik­rin­ti duomenų tipus

Kiek­vie­nas įvesties duomenų elementas turi atitikti numatytą duomenų tipą. Pa­vyz­džiui, jei rei­ka­lau­ja­ma įvesti skaičių, paprastas pa­tik­ri­ni­mas PHP kalba galėtų atrodyti taip:

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

Tokios patikros turėtų būti at­lie­ka­mos ir su eilutėmis, datomis ar kitais spe­ci­fi­niais formatais.

2. Filtruoti spe­cia­liuo­sius simbolius

Spe­cia­lie­ji simboliai gali sukelti saugumo pa­žei­džia­mu­mą, ypač SQL ar HTML kontekste. Saugus spren­di­mas – naudoti htmlspecialchars() HTML įvestims ir PDO::quote() SQL už­klau­soms.

3. Stenkitės nerodyti klaidų pranešimų

Reikėtų vengti tie­sio­gi­nių klaidų pranešimų, kuriuose at­sklei­džia­mi tech­ni­niai duomenų bazės ar sistemos duomenys. Vietoj to reikėtų rodyti bendro pobūdžio pranešimą, pa­vyz­džiui:

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

4. Naudokite paruoštus teiginius

Vienas iš veiks­min­giau­sių būdų išvengti SQL įterpimų – naudoti paruoštus teiginius. Taikant šį metodą, SQL komandos ir pa­ra­met­rai siunčiami atskirai, todėl pik­ta­va­liš­kas kodas negali būti vykdomas. Štai pa­vyz­di­nis įgy­ven­di­ni­mas PHP kalba naudojant PDO (PHP Data Objects):

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

Duomenų bazių valdymo sistema au­to­ma­tiš­kai užtikrina, kad įvesties duomenys būtų ap­do­ro­ja­mi saugiai.

2 žingsnis: Už­tik­rin­ki­te vi­sa­pu­siš­ką serverio apsaugą

Serverio, kuriame veikia jūsų duomenų bazių valdymo sistema, saugumas taip pat atlieka lemiamą vaidmenį užkertant kelią SQL įterpimui. Viena iš pag­rin­di­nių priemonių – su­stip­rin­ti operacinę sistemą, laikantis šių ge­riau­sios praktikos re­ko­men­da­ci­jų:

  • Įdiekite arba įjunkite tik tas programas ir paslaugas, kurios yra būtinos duomenų bazės veikimui.
  • Pa­ša­lin­ki­te visas ne­nau­do­ja­mas arba ne­rei­ka­lin­gas vartotojo paskyras.
  • Už­tik­rin­ki­te, kad visi rei­ka­lin­gi sistemos ir prog­ra­mi­nės įrangos at­nau­ji­ni­mai būtų įdiegiami ne­del­siant.
  • Taikykite mažiausių teisių principą, kad var­to­to­jams ir pa­slau­goms būtų suteiktos tik bū­ti­niau­sios teisės.

At­si­žvel­giant į jūsų in­ter­ne­ti­nio projekto saugumo rei­ka­la­vi­mus, reikėtų ap­svars­ty­ti pa­pil­do­mas apsaugos priemones:

  • Įsi­lau­žimų aptikimo sistemos (IDS) ir įsi­lau­žimų pre­ven­ci­jos sistemos (IPS): Šios sistemos naudoja įvairius aptikimo metodus, kad anksti nustatytų atakas, išsiųstų įspėjimus ir – naudojant IPS – au­to­ma­tiš­kai imtųsi at­sa­ko­mų­jų priemonių.
  • Tai­ko­mo­sios programos sluoksnio šliuzas (ALG): ALG stebi ir filtruoja srautą tarp taikomųjų programų ir ži­nia­tink­lio naršyklių tie­sio­giai tai­ko­mo­sios programos lygiu.
  • Tinklo programų užkarda (WAF): WAF spe­cia­liai apsaugo tinklo programas nuo SQL įterpimo ir tarp­ve­tai­nių skriptų (XSS), blo­kuo­da­ma arba valydama įtartinus už­klau­si­mus.
  • „Zero Trust“ metodas: šis modernus saugumo modelis užtikrina, kad kiek­vie­nas bandymas pri­si­jung­ti – ne­pri­klau­so­mai nuo jo kilmės – būtų pa­tik­rin­tas ir au­ten­tiš­ku­mas pa­tvir­tin­tas prieš su­tei­kiant prieigą.
  • Ug­nia­sie­nės taisyklės ir tinklo seg­men­ta­vi­mas: tai yra būtina norint il­ga­lai­kė­je per­spek­ty­vo­je sumažinti atakų paviršių.
  • Re­gu­lia­rūs IT saugumo auditai ir įsi­lau­žimo testai: jie padeda anksti aptikti ir pašalinti pa­žei­džia­mu­mą.

3 žingsnis: Už­tik­rin­ki­te duomenų bazės saugumą ir naudokite saugų kodą

Kaip ir operacinė sistema, duomenų bazė turi būti išvalyta nuo visų ne­rei­ka­lin­gų kom­po­nen­tų ir nuolat at­nau­ji­na­ma. Pa­ša­lin­ki­te visas ne­rei­ka­lin­gas saugomas pro­ce­dū­ras ir išjunkite visas ne­nau­do­ja­mas paslaugas bei vartotojų paskyras. Sukurkite specialią duomenų bazės paskyrą, skirtą tik prieigai per internetą, ir suteikite jai tik bū­ti­niau­sias teises.

At­si­žvel­giant į parengtų užklausų naudojimą, pri­myg­ti­nai re­ko­men­duo­ja­ma nenaudoti „PHP mysql modulio (kuris buvo pa­ša­lin­tas iš PHP 7). Vietoj to rinkitės mysqli arba PDO, kad už­tik­rin­tu­mė­te didesnį saugumą ir su­de­ri­na­mu­mą. Saugi mysqli užklausa galėtų atrodyti taip:

$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

Slap­ta­žo­džiai jokiu būdu neturėtų būti saugomi tie­sio­giai duomenų bazėje arba išsaugomi pa­prasto­jo teksto formatu. Vietoj to, naudokite maišymo metodą, pa­vyz­džiui, password_hash(), kartu su password_verify(), kad saugiai ap­sau­go­tu­mė­te pri­si­jun­gi­mo duomenis. Saugus įgy­ven­di­ni­mas galėtų atrodyti taip:

$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

Koks ryšys tarp „Bobby“ stalelių ir SQL įterpimo?

Svetainė bobby-tables.com naudoja in­ter­ne­ti­nį komiksą „xkcd“, kad juokingai iliust­ruo­tų pavojus, kylančius dėl nesaugių vartotojo įvesties duomenų duomenų bazėse. Komikse motina gauna skambutį iš savo sūnaus (mylimai vadinamo „Little Bobby Tables“) mokyklos. Skam­bin­to­jas paklausia, ar jos sūnaus vardas tikrai yra Robert'); DROP TABLE Students;--, o ji atsako, kad taip. Skambučio prie­žas­tis netrukus paaiškėja: bandant įvesti vardą „Robert“ į mokinių duomenų bazę, buvo ištrinta visa mokinių lentelė. Motina nėra pernelyg už­jau­čian­ti – ji tiesiog tikisi, kad mokykla išmoko pamoką ir ateityje valys duomenų bazės įvestis.

Ši ka­ri­ka­tū­ra aiškiai parodo ka­ta­st­ro­fiš­kas pasekmes, kurios gali kilti, jei duomenų bazių prog­ra­mo­se vartotojo įvestys nėra tinkamai pa­tik­rin­tos ir išvalytos.

Go to Main Menu