Mikä on SQL-injektio?
SQL-injektiot muodostavat merkittävän uhan relaatiotietokantamalleille ja niissä oleville arkaluonteisille tiedoille. Siksi kattava suojaus näitä luvattomia ulkoisia pääsyyrityksiä vastaan – jotka ovat mahdollisia tietoturva-aukkojen vuoksi – on ehdottoman välttämätöntä.
Mikä on SQL-injektio?
SQL-injektio on hyökkäystyyppi, jossa hyödynnetään tietoturva-aukkoa relaatiotietokantajärjestelmissä, jotka käyttävät SQL-kyselykieltä käyttäjän syöttämien tietojen käsittelyyn. Hyökkääjä hyödyntää käyttäjän syötteitä, joita ei ole suojattu asianmukaisesti ja jotka sisältävät erikoismerkkejä, kuten kaksoisviivoja, lainausmerkkejä tai puolipisteitä. Näillä merkeillä on erityisiä toimintoja SQL-tulkkissa, ja niiden avulla suoritettavia komentoja voidaan manipuloida ulkoisesti. SQL-injektiot liittyvät usein PHP- ja ASP-sovelluksiin, jotka perustuvat vanhentuneisiin rajapintoihin. Monissa näistä tapauksista syötettä ei puhdisteta riittävästi, mikä tekee siitä erinomaisen kohteen hyökkäyksille.
Lisäämällä strategisesti toimintomerkkejä luvaton käyttäjä voi syöttää järjestelmään ylimääräisiä SQL-komentoja ja manipuloida tietokantatietueita lukeakseen, muokatakseen tai poistaakseen tietoja. Pahimmissa tapauksissa hyökkääjät voivat jopa päästä käsiksi järjestelmän komentoriville, minkä seurauksena he voivat saada täyden hallinnan tietokantapalvelimesta.
Esimerkki SQL-injektiosta, joka havainnollistaa tietokantahyökkäyksen toimintaperiaatetta
Koska haavoittuvat tietokantapalvelimet voidaan tunnistaa nopeasti ja SQL-injektiohyökkäykset ovat suhteellisen helppoja toteuttaa, tämä menetelmä on edelleen yksi maailmanlaajuisesti yleisimmin käytetyistä tekniikoista kyberrikollisten keskuudessa. Hyökkääjät käyttävät erilaisia strategioita ja hyödyntävät sekä äskettäin löydettyjä että jo pitkään tunnettuja tietoturva-aukkoja tietojenhallintaprosessiin liittyvissä sovelluksissa. Jotta voimme paremmin ymmärtää, miten SQL-injektio toimii käytännössä, tarkastellaan esimerkkinä kahta yleistä hyökkäysmenetelmää.
Esimerkki 1: Pääsy huonosti suojatun käyttäjän syötteen kautta
Tietokantaan pääsemiseksi käyttäjien on yleensä ensin tunnistettava itsensä. Tätä varten käytetään yleisesti skriptejä, jotka tuottavat kirjautumislomakkeen, jossa on kentät käyttäjätunnukselle ja salasanalle. Käyttäjät täyttävät lomakkeen, ja skripti tarkistaa sitten, onko tietokannassa vastaavia tietueita. Oletusarvoisesti tietokanta saattaa sisältää taulukon nimeltä users, jossa on sarakkeet username ja password. Tyypillisessä verkkosovelluksessa tietokantaan pääsyä koskevat skriptirivit (Python-tyyppistä pseudokoodia käyttäen) saattavat näyttää tältä:
uname = request.POST['username']
passwd = request.POST['password']
sql = "SELECT id FROM users WHERE username='" + uname + "' AND password='" + passwd + "'"
database.execute(sql)pythonHyökkääjä voi nyt manipuloida salasanakenttää SQL-injektion avulla, esimerkiksi syöttämällä password' OR 1='1, mikä tuottaa seuraavan SQL-kyselyn:
sql = "SELECT id FROM users WHERE username='' AND password='password' OR 1='1'"pythonTämä antaa hyökkääjälle täyden pääsyn tietokannan koko käyttäjätaulukkoon, koska salasanakriteeri täyttyy aina (1='1'). Jos hyökkääjä kirjautuu sisään järjestelmänvalvojana, hän voi vapaasti muokata mitä tahansa tietokannan tietueita. Vaihtoehtoisesti käyttäjätunnuskenttää voidaan manipuloida samalla tavalla.
Esimerkki 2: Tietojen poiminta tunnisteiden muokkaamisen avulla
Tietojen hakeminen tietokannasta tunnisteen perusteella on käytännöllinen ja yleinen menetelmä, mutta se avaa myös mahdollisen portin SQL-injektiolle. Esimerkiksi verkkopalvelin tietää URL-osoitteessa lähetetyn tunnisteen perusteella, mitkä tiedot sen tulee hakea tietokannasta. Vastaava PHP-skripti näyttää tältä:
<?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);
}
?>phpOdotettu URL noudattaa mallia .../script.php?id=22. Tässä tapauksessa haettaisiin taulukon rivi, jonka tunniste on ”22”. Jos luvaton henkilö pääsee muokkaamaan tätä URL-osoitetta ja lähettää sen sijaan pyynnön, kuten .../script.php?id=22+OR+1=1, tuloksena oleva kysely aiheuttaa sen, että taulukon kaikki rivit haetaan:
SELECT * FROM table WHERE id=22 OR 1=1;sqlMiten rikolliset löytävät haavoittuvia tietokantajärjestelmiä?
Periaatteessa mikä tahansa verkkosivusto tai verkkosovellus, joka käyttää SQL-tietokantoja ilman esivalmisteltuja kyselyitä (prepared statements) tai muita suojatoimenpiteitä, voi olla alttiina SQL-injektioille. Löydetyt haavoittuvuudet eivät pysy kauan piilossa internetissä. Itse asiassa on olemassa verkkosivustoja, jotka julkaisevat ajantasaisia luetteloita tunnetuista tietoturva-aukkoista – ja jopa selittävät, kuinka hyökkääjät voivat käyttää Google-hakuja löytääkseen vastaavia verkkoprojekteja. Jos verkkosivusto palauttaa yksityiskohtaisia SQL-virheilmoituksia, kyberrikolliset voivat käyttää näitä viestejä tunnistaakseen mahdollisia haavoittuvuuksia. Esimerkiksi apostrofin lisääminen URL-osoitteen loppuun, joka sisältää ID-parametrin, voi jo paljastaa heikkouden, kuten seuraavassa esimerkissä on esitetty:
[DomainName].com/news.php?id=5’Haavoittuva verkkosivusto palauttaa virheilmoituksen seuraavassa muodossa:
Query failed: You have an error in your SQL syntax…
Samanlaisia menetelmiä voidaan käyttää myös sarakkeiden lukumäärän, taulukko- ja sarakkeenimien, SQL-version tai jopa käyttäjätunnusten ja salasanojen selvittämiseen. Lisäksi on olemassa erilaisia työkaluja, joilla voidaan automatisoida sekä tietojen keräämisprosessi että SQL-injektiohyökkäysten toteuttaminen.
Kuinka suojata tietokantaasi SQL-injektiolta
Tietokantajärjestelmääsi kohdistuvia SQL-injektiohyökkäyksiä voidaan estää monin eri tavoin. Sinun tulee huomioida kaikki asiaan liittyvät osatekijät – palvelin ja yksittäiset sovellukset sekä tietokannan hallintajärjestelmä.
Vaihe 1: Seuraa sovelluksista tulevia automaattisia syötteitä
Kun käsitellään ulkoisista tai sisäänrakennetuista sovelluksista tulevia syötteitä, on ehdottomasti tarkistettava ja suodatettava lähetetyt arvot SQL-hyökkäysten estämiseksi.
1. Tarkista tietotyypit
Jokaisen syötteen on vastattava odotettua tietotyyppiä. Jos esimerkiksi vaaditaan numeerista syötettä, yksinkertainen validointi PHP:ssä voisi näyttää tältä:
if (filter_var($input, FILTER_VALIDATE_INT) === false) {
throw new InvalidArgumentException("Invalid input");
}phpVastaavia tarkistuksia tulisi toteuttaa merkkijonojen, päivämäärien tai muiden erityisten muotojen osalta.
2. Suodata erikoismerkit
Erikoismerkit voivat aiheuttaa tietoturva-aukkoja, etenkin SQL- tai HTML-yhteyksissä. Turvallinen tapa on käyttää htmlspecialchars() HTML-syötteissä ja PDO::quote() SQL-kyselyissä.
3. Vältä virheilmoitusten näyttämistä
Tietokannan tai järjestelmän teknisiä yksityiskohtia paljastavia suoria virheilmoituksia tulisi välttää. Sen sijaan tulisi näyttää yleinen viesti, kuten:
echo "An error occurred. Please try again later.";
error_log("Unexpected error encountered. See system log for details.");php4. Käytä valmiita lauseita
Yksi tehokkaimmista tavoista estää SQL-injektiot on käyttää valmiita lauseita. Tässä menetelmässä SQL-komennot ja parametrit lähetetään erikseen, joten haitallista koodia ei voida suorittaa. Tässä on esimerkki toteutuksesta PHP:llä PDO:ta (PHP Data Objects) käyttäen:
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->bindParam(':id', $user_id, PDO::PARAM_INT);
$stmt->execute();phpTietokannan hallintajärjestelmä varmistaa automaattisesti, että syötetyt tiedot käsitellään turvallisesti.
Vaihe 2: Varmista palvelimen kattava suojaus
Myös sen palvelimen tietoturva, jolla tietokantajärjestelmäsi toimii, on ratkaisevassa asemassa SQL-injektioiden torjunnassa. Keskeinen toimenpide on käyttöjärjestelmän suojaaminen noudattamalla seuraavia parhaita käytäntöjä:
- Asenna tai ota käyttöön vain ne sovellukset ja palvelut, jotka ovat välttämättömiä tietokannan toiminnalle.
- Poista kaikki käyttämättömät tai tarpeettomat käyttäjätilit.
- Varmista, että kaikki tarvittavat järjestelmä- ja ohjelmistopäivitykset asennetaan viipymättä.
- Sovella vähimmäisoikeuksien periaatetta varmistaaksesi, että käyttäjille ja palveluille myönnetään vain välttämättömät vähimmäisoikeudet.
Verkkoprojektisi turvallisuusvaatimusten mukaan sinun tulisi harkita seuraavia lisäsuojatoimenpiteitä:
- Tunkeutumisen havaitsemisjärjestelmät (IDS) ja tunkeutumisen estojärjestelmät (IPS): Nämä järjestelmät käyttävät erilaisia havaitsemismenetelmiä hyökkäysten varhaiseen tunnistamiseen, hälytysten lähettämiseen ja – IPS-järjestelmän ollessa käytössä – vastatoimenpiteiden automaattiseen käynnistämiseen.
- Sovelluskerroksen yhdyskäytävä (ALG): ALG valvoo ja suodattaa sovellusten ja verkkoselainten välistä liikennettä suoraan sovellustasolla.
- Verkkosovellusten palomuuri (WAF): WAF suojaa erityisesti verkkosovelluksia SQL-injektiolta ja sivustojen väliseltä skriptaukselta (XSS) estämällä tai puhdistamalla epäilyttävät pyynnöt.
- Zero Trust -lähestymistapa: Tämä moderni tietoturvamalli varmistaa, että jokainen pääsyyritys – sen alkuperästä riippumatta – tarkistetaan ja todennetaan ennen kuin pääsy myönnetään.
- Palomuurisäännöt ja verkon segmentointi: Nämä ovat välttämättömiä hyökkäyspinnan minimoimiseksi pitkällä aikavälillä.
- Säännölliset IT-tietoturvatarkastukset ja tunkeutumistestit: Nämä auttavat havaitsemaan ja korjaamaan haavoittuvuudet varhaisessa vaiheessa.
Vaihe 3: Suojaa tietokanta ja käytä turvallista koodia
Aivan kuten käyttöjärjestelmänkin, myös tietokannasta tulisi poistaa kaikki tarpeettomat osat ja pitää se ajan tasalla. Poista kaikki tarpeettomat tallennetut menettelyt ja poista käytöstä kaikki käyttämättömät palvelut ja käyttäjätilit. Luo erillinen tietokantatili, joka on tarkoitettu yksinomaan verkkokäyttöön, ja myönnä sille vain välttämättömät käyttöoikeudet.
Valmiiden lauseiden käytön mukaisesti on erittäin suositeltavaa olla käyttämättä mysql (joka poistettiin PHP 7:stä). Valitse sen sijaan mysqli tai PDO, jotta varmistat paremman tietoturvan ja yhteensopivuuden. Turvallinen mysqli voisi näyttää tältä:
$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();phpSalasanoja ei saa koskaan tallentaa suoraan tietokantaan tai hakea selväkielisenä. Sen sijaan kannattaa käyttää hajautusmenetelmää, kuten password_hash(), yhdessä password_verify() kanssa käyttäjätunnusten suojaamiseksi turvallisesti. Turvallinen toteutus voisi näyttää tältä:
$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.";
}phpMitä yhteistä bobby-pöydillä on SQL-injektion kanssa?
Verkkosivusto bobby-tables.com käyttää xkcd-verkkosarjakuvia havainnollistaakseen humoristisesti tietokantoihin syötettävien suojaamattomien käyttäjätietojen vaaroja. Sarjakuvassa äiti saa puhelun poikansa (jota kutsutaan hellästi nimellä Little Bobby Tables) koulusta. Soittaja kysyy, onko pojan nimi todella Robert'); DROP TABLE Students;--, mihin äiti vastaa myöntävästi. Soiton syy selviää pian: yritys syöttää nimi Robert oppilasrekisteriin johti koko oppilastietokannan poistumiseen. Äiti ei ole kovin myötätuntoinen – hän toivoo vain, että koulu on oppinut läksynsä ja puhdistaa jatkossa tietokantaan syötettävät tiedot.
Sarjakuva tuo selvästi esiin ne katastrofaaliset seuraukset, joita voi aiheutua siitä, ettei käyttäjän syöttämiä tietoja tarkisteta ja puhdisteta asianmukaisesti tietokantasovelluksissa.