Deci crezi că baza de date SQL este performantă și sigură de distrugerea instantanee? Ei bine, SQL Injection nu este de acord!


Da, despre distrugerea instantanee despre care vorbim, pentru că nu vreau să deschid acest articol cu ​​terminologia obișnuită a șchiopărării de „strângere a securității” și „prevenirea accesului rău intenționat”. SQL Injection este un truc atât de vechi din carte, încât toată lumea, fiecare dezvoltator, cunoaște foarte bine despre aceasta și cunoaște bine modul de prevenire. Cu excepția acelei perioade ciudate, atunci când acestea alunecă, iar rezultatele nu pot fi nimic dezastruos.

Dacă știți deja ce este SQL Injection, nu ezitați să treceți la jumătatea din urmă a articolului. Dar pentru cei care abia vin în domeniul dezvoltării web și visează să asume mai multe roluri superioare, o anumită introducere este în ordine.

Ce este SQL Injection?

Cheia pentru înțelegerea SQL Injection este în numele său: SQL + Injection. Cuvântul „injecție” aici nu are nicio conotație medicală, ci este mai degrabă utilizarea verbului „inject”. Împreună, aceste două cuvinte transmit ideea de a introduce SQL într-o aplicație web.

Introducerea SQL într-o aplicație web. . . hmmm. . . Oare nu facem așa ceva? Da, dar nu dorim ca un atacator să ne conducă baza de date. Să înțelegem asta cu ajutorul unui exemplu.

Să presupunem că construiți un site web PHP tipic pentru un magazin local de comerț electronic, deci decideți să adăugați un formular de contact ca acesta:

Numele dumneavoastră

Mesajul tau

Și să presupunem că fișierul send_message.php stochează totul într-o bază de date, astfel încât proprietarii magazinului să poată citi mai târziu mesajele utilizatorului. Este posibil să aibă un cod ca acesta:

<?php

$ nume = $ _POST [‘nume’];
$ mesaj = $ _POST [‘mesaj’];

// verificați dacă acest utilizator are deja un mesaj
mysqli_query ($ conn, "SELECT * din mesajele unde nume = $ nume");

// Alt cod aici

Așadar, încercați mai întâi să vedeți dacă acest utilizator are deja un mesaj necitit. Interogarea SELECT * din mesajele unde nume = $ nume pare destul de simplu, corect?

GRESIT!

În inocența noastră, am deschis ușile pentru distrugerea instantanee a bazei noastre de date. Pentru ca acest lucru să se întâmple, atacatorul trebuie să îndeplinească următoarele condiții:

  • Aplicația rulează pe o bază de date SQL (astăzi, aproape fiecare aplicație este)
  • Conexiunea curentă a bazei de date are permisiuni „edit” și „ștergere” din baza de date
  • Numele tabelelor importante pot fi ghicite

Al treilea punct înseamnă că acum, când atacatorul știe că conduci un magazin de comerț electronic, este foarte probabil să stochezi datele comenzii într-un tabel de comenzi. Înarmat cu toate acestea, tot atacatorul trebuie să facă este să îi furnizeze numele lor:

Joe; trunchiate comenzi ;? Da domnule! Să vedem ce va deveni interogarea atunci când va fi executată de scriptul PHP:

SELECTA * DIN mesajele WHERE nume = Joe; trunchiere comenzi;

Bine, prima parte a interogării are o eroare de sintaxă (fără ghilimele în jurul „Joe”), dar semi-colonul forțează motorul MySQL să înceapă să interpreteze unul nou: trunchiul comenzilor. La fel, într-o singură clipă, întregul istoric al comenzilor a dispărut!

După ce știi cum funcționează SQL Injection, este timpul să te uiți la modul de oprire. Cele două condiții care trebuie îndeplinite pentru o injecție SQL de succes sunt:

  1. Scriptul PHP ar trebui să aibă privilegii de modificare / ștergere în baza de date. Cred că acest lucru este valabil pentru toate aplicațiile și nu veți putea face aplicațiile dvs. doar în citire. �� Și ghiciți ce, chiar dacă eliminăm toate privilegiile de modificare, injecția SQL poate permite oricui să ruleze interogări SELECT și să vizualizeze toate bazele de date, datele sensibile incluse. Cu alte cuvinte, reducerea nivelului de acces la baza de date nu funcționează și aplicația dvs. oricum are nevoie de ea.
  2. Intrarea utilizatorului este procesată. Singura modalitate de funcționare a injecției SQL este atunci când acceptați date de la utilizatori. Încă o dată, nu este practic să opriți toate intrările pentru aplicația dvs. doar pentru că sunteți îngrijorat de injecția SQL.

Prevenirea injecției SQL în PHP

Acum, având în vedere că conexiunile la baze de date, interogările și intrările utilizatorului fac parte din viață, cum putem preveni injecția SQL? Din fericire, este destul de simplu și există două modalități de a face acest lucru: 1) igienizați intrarea utilizatorului și 2) folosiți declarații pregătite.

Sanitizează intrarea utilizatorului

Dacă utilizați o versiune PHP mai veche (5.5 sau mai mică, iar acest lucru se întâmplă foarte mult la găzduirea partajată), este înțelept să rulați toată intrarea utilizatorului printr-o funcție numită mysql_real_escape_string (). Practic, ceea ce face elimină toate caracterele speciale dintr-un șir, astfel încât să își piardă sensul atunci când sunt utilizate de baza de date.

De exemplu, dacă aveți un șir cum ar fi un șir, caracterul unic de cotație (‘) poate fi folosit de un atacator pentru a manipula interogarea bazei de date creată și a provoca o injecție SQL. Rularea acestuia prin mysql_real_escape_string () produce I \ ‘m un șir, care adaugă o întoarcere la cota unică, scăpându-l. Drept urmare, întreaga șir este acum transmisă ca un șir inofensiv pentru baza de date, în loc să poată participa la manipularea interogărilor.

Există un singur dezavantaj cu această abordare: este o tehnică cu adevărat veche, care merge împreună cu formele mai vechi de acces la baza de date în PHP. Începând cu PHP 7, această funcție nu mai există niciodată, ceea ce ne aduce la următoarea noastră soluție.

Folosiți enunțuri pregătite

Declarațiile pregătite sunt o modalitate de a face interogările la baza de date mai sigure și mai fiabile. Ideea este că, în loc să trimiteți interogarea brută la baza de date, vom spune mai întâi bazei de date structura interogării pe care o vom trimite. La asta ne referim prin „pregătirea” unei afirmații. Odată pregătită o declarație, transmitem informațiile ca intrări parametrizate, astfel încât baza de date să poată „umple golurile” prin conectarea intrărilor la structura de interogare pe care am trimis-o înainte. Aceasta elimină orice putere specială pe care o pot avea intrările, determinând ca acestea să fie tratate ca simple variabile (sau sarcini utile, dacă veți face acest lucru) în întregul proces. Iată cum arată declarațiile pregătite:

<?php
$ servername = "gazdă locală";
$ nume de utilizator = "nume de utilizator";
$ parolă = "parola";
$ dbname = "mydb";

// Creați conexiune
$ conn = new mysqli ($ nume, nume de utilizator $, parolă $, nume $ db);

// Verificați conexiunea
if ($ conn->conecta_error) {
a muri("Conexiune esuata: " . $ conn->connect_error);
}

// pregătiți și legați
$ stmt = $ conn->a pregati("INSERTĂ ÎN MyGuests (prenume, nume, e-mail) VALORI (?,?,?)");
$ STMT->bind_param ("SSS", $ prenume, $ prenume, $ email);

// setați parametrii și executați
$ prenume = "Ioan";
$ lastname = "Căprioară";
$ e-mail = "[Email protected]";
$ sTMT->a executa();

$ prenume = "Mary";
$ lastname = "Moe";
$ e-mail = "[Email protected]";
$ sTMT->a executa();

$ prenume = "Julie";
$ lastname = "Dooley";
$ e-mail = "[Email protected]";
$ sTMT->a executa();

ecou "Înregistrări noi create cu succes";

$ sTMT->închide();
$ conn->închide();
?>

Știu că procesul sună inutil de complex dacă sunteți nou la declarații pregătite, dar conceptul merită mult efortul. Iată o introducere drăguță la ea.

Pentru cei care sunt deja familiarizați cu extensia DPO a PHP și care o folosesc pentru a crea declarații pregătite, am un mic sfat.

Avertisment: Aveți grijă când configurați PDO

Când utilizăm PDO pentru acces la baze de date, putem fi sugerați într-un fals sentiment de securitate. „Ah, bine, folosesc DPO. Acum nu trebuie să mă gândesc la nimic altceva ”- așa merge în general gândirea noastră. Este adevărat că PDO (sau declarațiile MySQLi pregătite) este suficient pentru a preveni tot felul de atacuri de injecție SQL, dar trebuie să fii atent atunci când îl configurezi. Este obișnuit să pur și simplu copiați-lipiți codul din tutorialele sau proiectele anterioare și să continuați, dar această setare poate anula totul:

$ dbConnection->setAttribute (PDO :: ATTR_EMULATE_PREPARES, adevărat);

Ce face această setare este de a spune PDO să imite declarații pregătite, mai degrabă decât să utilizeze efectiv funcția de pregătire a bazei de date. În consecință, PHP trimite șiruri simple de interogare către baza de date, chiar dacă codul dvs. pare că creează declarații pregătite și parametri de setare și toate acestea. Cu alte cuvinte, ești atât de vulnerabil la injecția SQL ca înainte. ��

Soluția este simplă: asigurați-vă că această emulație este setată pe false.

$ dbConnection->setAttribute (PDO :: ATTR_EMULATE_PREPARES, fals);

Acum scriptul PHP este obligat să folosească instrucțiuni pregătite la nivel de bază de date, prevenind tot felul de injecții SQL.

Prevenirea utilizării WAF

Știți că puteți proteja și aplicațiile web de injecția SQL folosind WAF (firewall pentru aplicații web)?

Ei bine, nu doar injecția SQL, ci multe alte vulnerabilități de nivel 7, cum ar fi scripturi încrucișate, autentificare spartă, falsificare a site-urilor, expunere a datelor, etc. Puteți utiliza self-gazdate, precum Mod Security sau cloud-based, precum următoarele:.

Injecție SQL și cadre moderne PHP

Injecția SQL este atât de comună, atât de ușoară, atât de frustrantă și atât de periculoasă încât toate cadrele web PHP moderne sunt încorporate cu contramăsuri. În WordPress, de exemplu, avem $ wpdb->prepare () funcție, în timp ce, dacă folosiți un cadru MVC, face toate lucrurile murdare pentru dvs. și nici nu trebuie să vă gândiți la prevenirea injecției SQL. Este puțin enervant faptul că în WordPress trebuie să pregătești declarații în mod explicit, dar hei, despre WordPress vorbim. ��

În orice caz, ideea mea este că rasa modernă de dezvoltatori web nu trebuie să se gândească la injecția SQL și, prin urmare, nici măcar nu sunt conștienți de această posibilitate. Ca atare, chiar dacă lasă una din spate deschisă în aplicația lor (poate că este un parametru de interogare $ _GET și obiceiuri vechi de a lansa o lovitură de interogare murdară), rezultatele pot fi catastrofale. Așadar, este întotdeauna mai bine să îți faci timp pentru a te afunda mai adânc în temelii.

Concluzie

SQL Injection este un atac foarte urât la o aplicație web, dar este ușor de evitat. Așa cum am văzut în acest articol, fiind atent atunci când prelucrăm introducerea utilizatorului (apropo, SQL Injection nu este singura amenințare pe care o aduce manipularea intrării utilizatorului) și interogarea bazei de date este tot ce există. Acestea fiind spuse, nu lucrăm întotdeauna în securitatea unui cadru web, așa că este mai bine să fim conștienți de acest tip de atac și să nu cădem pentru asta.

Jeffrey Wilson Administrator
Sorry! The Author has not filled his profile.
follow me