Tātad jūs domājat, ka jūsu SQL datu bāze ir efektīva un droša pret tūlītēju iznīcināšanu? Nu, SQL injekcija nepiekrīt!
Jā, mēs runājam par tūlītēju iznīcināšanu, jo es nevēlos atvērt šo rakstu ar parasto lamu terminoloģiju “drošības pastiprināšana” un “ļaunprātīgas piekļuves novēršana”. SQL injekcija ir tik vecs triks grāmatā, ka visi, katrs izstrādātājs, par to ļoti labi zina un labi zina, kā to novērst. Izņemot to vienreizējo reizi, kad tie paslīd, un rezultāti var būt ne mazāk kā postoši.
Ja jūs jau zināt, kas ir SQL injekcija, varat pāriet uz raksta otro pusi. Bet tiem, kas tikai nāk uz priekšu tīmekļa izstrādes jomā un sapņo uzņemties augstākā līmeņa amatus, ir lietderīgi iepazīstināt.
Kas ir SQL injekcija?
SQL injekcijas izpratnes atslēga ir tās nosaukumā: SQL + Injection. Vārdam “injekcija” šeit nav nekādas medicīniskas nozīmes, bet gan tas ir darbības vārda “inject” lietojums. Šie divi vārdi kopā atspoguļo ideju par SQL ievietošanu tīmekļa lietojumprogrammā.
SQL ievietošana tīmekļa lietojumprogrammā. . . hmmm . . . Vai tik un tā mēs to nedarām? Jā, bet mēs nevēlamies, lai uzbrucējs vadītu mūsu datu bāzi. Sapratīsim to ar piemēra palīdzību.
Pieņemsim, ka veidojat tipisku PHP vietni vietējam e-komercijas veikalam, tāpēc izlemjat pievienot šādu saziņas veidlapu:
<form action="record_message.php" method="POST"> <label>Your name</label> <input type="text" name="name"> <label>Your message</label> <textarea name="message" rows="5"></textarea> <input type="submit" value="Send"> </form>
Un pieņemsim, ka fails send_message.php visu saglabā datu bāzē, lai veikala īpašnieki vēlāk varētu lasīt lietotāja ziņojumus. Tam var būt šāds kods:
<?php $name = $_POST['name']; $message = $_POST['message']; // check if this user already has a message mysqli_query($conn, "SELECT * from messages where name = $name"); // Other code here
Tātad jūs vispirms mēģināt noskaidrot, vai šim lietotājam jau nav nelasīts ziņojums. Vaicājums SELECT * no ziņojumiem, kur vārds = $name šķiet pietiekami vienkāršs, vai ne?
NEPAREIZI!
Savā nevainībā mēs esam pavēruši durvis tūlītējai mūsu datubāzes iznīcināšanai. Lai tas notiktu, uzbrucējam ir jāizpilda šādi nosacījumi:
- Lietojumprogramma darbojas SQL datu bāzē (šodien gandrīz katra lietojumprogramma darbojas)
- Pašreizējam datu bāzes savienojumam ir datu bāzes “rediģēšanas” un “dzēšanas” atļaujas
- Svarīgo tabulu nosaukumus var uzminēt
Trešais punkts nozīmē, ka tagad, kad uzbrucējs zina, ka jums ir e-komercijas veikals, jūs, ļoti iespējams, glabājat pasūtījuma datus pasūtījumu tabulā. Apbruņojoties ar visu šo, uzbrucējam ir tikai jānorāda šāds vārds:
Džo; saīsināt pasūtījumus;? Jā, ser! Apskatīsim, kāds vaicājums kļūs, kad to izpildīs PHP skripts:
SELECT * FROM ziņojumi WHERE vārds = Džo; saīsināt pasūtījumus;
Labi, vaicājuma pirmajā daļā ir sintakses kļūda (ap „Džo” nav pēdiņu), taču semikols liek MySQL dzinējam sākt interpretēt jaunu: saīsināt pasūtījumus. Tieši tāpat vienā piegājienā visa pasūtījumu vēsture ir pazudusi!
Tagad, kad zināt, kā darbojas SQL injection, ir pienācis laiks apsvērt, kā to apturēt. Divi nosacījumi, kas jāizpilda veiksmīgai SQL injekcijai, ir šādi:
SQL injekcijas novēršana PHP
Tagad, ņemot vērā, ka datu bāzes savienojumi, vaicājumi un lietotāju ievades ir daļa no dzīves, kā novērst SQL ievadīšanu? Par laimi, tas ir diezgan vienkārši, un ir divi veidi, kā to izdarīt: 1) tīrīt lietotāja ievadīto informāciju un 2) izmantot sagatavotos paziņojumus.
Dezinficējiet lietotāja ievadi
Ja izmantojat vecāku PHP versiju (5.5 vai vecāku, un tas bieži notiek dalītā mitināšanā), ir saprātīgi palaist visu lietotāja ievadi, izmantojot funkciju mysql_real_escape_string(). Būtībā tas, ko tas dara, virknē noņem visas īpašās rakstzīmes, lai tās zaudētu nozīmi, kad tās izmanto datu bāze.
Piemēram, ja jums ir tāda virkne kā es esmu virkne, uzbrucējs var izmantot vienas pēdiņas rakstzīmi (‘), lai manipulētu ar izveidojamo datu bāzes vaicājumu un izraisītu SQL injekciju. Palaižot to caur mysql_real_escape_string() rada I’m virkni, kas pievieno slīpsvītru vienai pēdiņai, izvairoties no tā. Rezultātā visa virkne tagad tiek nodota datu bāzei kā nekaitīga virkne, tā vietā, lai varētu piedalīties vaicājumu manipulācijās.
Šai pieejai ir viens trūkums: tā ir patiešām, ļoti veca tehnika, kas iet kopā ar vecākām PHP piekļuves formām datu bāzēm. Sākot ar PHP 7, šī funkcija vairs pat nepastāv, un tas mūs noved pie nākamā risinājuma.
Izmantojiet sagatavotos paziņojumus
Sagatavotie paziņojumi ir veids, kā padarīt datubāzes vaicājumus drošākus un uzticamākus. Ideja ir tāda, ka tā vietā, lai nosūtītu neapstrādātu vaicājumu uz datu bāzi, mēs vispirms sniedzam datubāzei nosūtāmā vaicājuma struktūru. Tas ir tas, ko mēs domājam ar paziņojuma “sagatavošanu”. Kad paziņojums ir sagatavots, mēs nododam informāciju kā parametrizētas ievades, lai datu bāze varētu “aizpildīt nepilnības”, pievienojot ievades vaicājuma struktūrai, kuru mēs iepriekš nosūtījām. Tas atņem jebkādu īpašo jaudu, kas varētu būt ievades datiem, liekot tiem visā procesā uzskatīt tos tikai kā mainīgos (vai lietderīgās slodzes, ja vēlaties). Lūk, kā izskatās sagatavotie paziņojumi:
<?php $servername = "localhost"; $username = "username"; $password = "password"; $dbname = "myDB"; // Create connection $conn = new mysqli($servername, $username, $password, $dbname); // Check connection if ($conn->connect_error) { die("Connection failed: " . $conn->connect_error); } // prepare and bind $stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)"); $stmt->bind_param("sss", $firstname, $lastname, $email); // set parameters and execute $firstname = "John"; $lastname = "Doe"; $email = "[email protected]"; $stmt->execute(); $firstname = "Mary"; $lastname = "Moe"; $email = "[email protected]"; $stmt->execute(); $firstname = "Julie"; $lastname = "Dooley"; $email = "[email protected]"; $stmt->execute(); echo "New records created successfully"; $stmt->close(); $conn->close(); ?>
Es zinu, ka process izklausās nevajadzīgi sarežģīts, ja esat iesācējs sagatavotu paziņojumu jomā, taču koncepcija ir pūļu vērta. Lūk jauks ievads tajā.
Tiem, kas jau ir iepazinušies ar PHP ACVN paplašinājumu un izmanto to sagatavotu paziņojumu veidošanai, man ir neliels padoms.
Brīdinājums: esiet piesardzīgs, iestatot ACVN
Izmantojot ACVN piekļuvei datu bāzei, mēs varam iesūkties nepatiesā drošības sajūtā. “Ak, es izmantoju ACVN. Tagad man vairs ne par ko citu nav jādomā” — tā parasti notiek mūsu domāšana. Taisnība, ka ar ACVN (vai MySQLi sagatavotajiem paziņojumiem) pietiek, lai novērstu visa veida SQL injekcijas uzbrukumus, taču jums jābūt uzmanīgiem, iestatot to. Ierasts vienkārši kopēt un ielīmēt kodu no apmācībām vai iepriekšējiem projektiem un turpināt darbu, taču šis iestatījums var atsaukt visu:
$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
Šis iestatījums liek ACVN atdarināt sagatavotos paziņojumus, nevis faktiski izmantot sagatavoto paziņojumu līdzekli datubāzē. Līdz ar to PHP datu bāzei nosūta vienkāršas vaicājumu virknes, pat ja jūsu kods izskatās tā, it kā tas veido sagatavotus paziņojumus un iestata parametrus un visu to. Citiem vārdiem sakot, jūs esat tikpat neaizsargāts pret SQL injekciju kā iepriekš. 🙂
Risinājums ir vienkāršs: pārliecinieties, vai šī emulācija ir iestatīta uz nepatiesu.
$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
Tagad PHP skripts ir spiests izmantot sagatavotus paziņojumus datu bāzes līmenī, novēršot visa veida SQL injekcijas.
WAF izmantošanas novēršana
Vai zināt, ka varat arī aizsargāt tīmekļa lietojumprogrammas no SQL ievadīšanas, izmantojot WAF (tīmekļa lietojumprogrammu ugunsmūri)?
Ne tikai SQL injekcija, bet arī daudzas citas 7. līmeņa ievainojamības, piemēram, starpvietņu skriptēšana, bojāta autentifikācija, vairāku vietņu viltošana, datu ekspozīcija utt. Varat izmantot pašizmitinātos, piemēram, Mod Security, vai mākoņdatošanas veidus, kā norādīts tālāk.
SQL injekcija un mūsdienīgi PHP ietvari
SQL ievadīšana ir tik izplatīta, tik vienkārša, tik nomākta un tik bīstama, ka visos mūsdienu PHP tīmekļa ietvaros ir iebūvēti pretpasākumi. Piemēram, programmā WordPress mums ir funkcija $wpdb->prepare(), savukārt, ja izmantojat MVC ietvaru, tā visu netīro darbu veic jūsu vietā un jums pat nav jādomā par SQL injekcijas novēršanu. Tas ir nedaudz kaitinoši, ka programmā WordPress jums ir skaidri jāsagatavo paziņojumi, bet hei, mēs runājam par WordPress. 🙂
Jebkurā gadījumā mana doma ir tāda, ka mūsdienu tīmekļa izstrādātājiem nav jādomā par SQL ievadīšanu, un rezultātā viņi pat neapzinās šo iespēju. Tādējādi, pat ja viņi savā lietojumprogrammā atstāj atvērtu vienu aizmugures durvis (varbūt tas ir $_GET vaicājuma parametrs un vecie ieradumi aktivizēt netīro vaicājumu), rezultāti var būt katastrofāli. Tāpēc vienmēr ir labāk veltīt laiku, lai ienirt dziļāk pamatos.
Secinājums
SQL injekcija ir ļoti nepatīkams uzbrukums tīmekļa lietojumprogrammai, taču no tā ir viegli izvairīties. Kā mēs redzējām šajā rakstā, jābūt uzmanīgiem, apstrādājot lietotāja ievadi (starp citu, SQL injekcija nav vienīgais drauds, ko rada lietotāja ievades apstrāde) un datubāzes vaicājumu veikšana. Tomēr mēs ne vienmēr strādājam ar tīmekļa ietvara drošību, tāpēc labāk ir apzināties šāda veida uzbrukumus un neļauties tam.