Încredere, dar verificați: protecția împotriva injecțiilor SQL
Fără îndoială, injecțiile SQL sunt una dintre cele mai comune metode de hacking a unui site web. Primul lucru pe care crapatorul încearcă să îl facă este să testeze injecțiile populare. În acest post scurt, vom revedea pe scurt istoria materiei, metode de a face cu preparate injectabile, și scrie un mic PHP-clasă este un înveliș pentru PDOStatement pentru a se conecta în siguranță și să interacționeze cu MySQL-server (MySQL, în acest caz, este pur și simplu pentru că cea mai mare prevalenta, dacă doriți toate caracteristicile următoare pot fi adaptate la alte DBMS-uri).
Ce, în esență, este injecția? Un dezvoltator PHP incepator, care tocmai a inceput sa studieze MySQL, va construi cel mai probabil interogari in urmatorul mod:
În cazul unui utilizator bun, scriptul va funcționa exact așa cum a fost intenționat. Ce va încerca atacatorul? Nu va trimite o valoare numerică serverului. În schimb, el va încerca să pună punct și virgulă după valoarea numerică (simbolul pentru completarea interogării SQL), apoi își va scrie propria, care nu are deja o interogare care a fost inițial concepută de noi. Ceva de genul asta:
sau chiar mai rău:
Ca rezultat, serverul MySQL va executa mai întâi solicitarea noastră, iar apoi cererea atacatorului. Astfel, un atacator, în funcție de situație (și drepturile de utilizator MySQL care utilizează PHP) pentru a face aproape orice, să aibă acces la informații confidențiale controlul complet al bazei de date.
Cum dezvoltatorul protejează site-ul de injecții?
Verificarea manuală a datelor utilizatorului. Desigur, verificarea formatului de intrare este prima formă cea mai evidentă de protecție împotriva injectărilor. În loc să avem încredere în datele de intrare, le vom verifica pentru a se potrivi cu formatul de care avem nevoie. Puteți face acest lucru într-o duzină de moduri diferite. De exemplu:
În acest caz, informațiile primite de la GET sunt verificate pentru o valoare numerică înainte de a fi trimise. Orice altă valoare decât valoarea numerică introdusă de atacator nu va fi trimisă la serverul MySQL. Pentru a valida valori mai complexe, puteți utiliza expresii regulate.
O altă variantă a protecției primitive este folosirea funcției native mysqli_real string_escape () pentru a împiedica pătrunderea cererilor "corupte". Principalul dezavantaj al acestei abordări este neautomatizarea extremă: trebuie să verificăm valoarea utilizatorului de fiecare dată când facem o solicitare. Puteți, bineînțeles, să utilizați un constructor SQL pregătit cu protecție integrată împotriva injecțiilor SQL sau să scrieți propriile dvs. Cu toate acestea, în acest post oferim o abordare ușor diferită bazată pe utilizarea DOP.
Începând cu versiunea 5.1, PHP are o clasă PDO încorporată (PHP Data Objects). Această clasă conține un set bogat de metode pentru a lucra cu o gamă largă de baze de date. În ciuda vârstei înaintate a instrumentului, mulți dezvoltatori ignoră, preferând „de modă veche“ pentru a utiliza biblioteca mysqli, inclusiv noi în DLE, dar avem o serie de motive importante, DLE este un scenariu vechi, care a apărut cu mult înainte de DOP, și avem un angajament de compatibilitate cu versiuni mai vechi ale script-ul, precum și privind simplificarea maximă a procesului de actualizare, pentru cei care folosesc module terțe părți. În plus, abordăm foarte atent problemele de filtrare a datelor primite. Dar, spre deosebire de tine noi nu suntem atât de vechi „dinozauri“, astfel încât ideea principală dorim să vă transmitem este aceasta: utilizarea mysqli directă, fără nici ambalaje - un mod direct acest SQL injectare, astfel încât să scrie cod o dată în condiții de siguranță. Dezvoltatorii, de fapt, există doar trei căi:
- utilizați verificarea manuală a datelor utilizatorului
- Scrieți propria dvs. bibliotecă (sau luați-o) bazată pe mysqli cu protecție SQL de injecție
- folosiți DOP
Clasa noastră va conține doar 4 metode:
- pentru conectarea la baza de date
- pentru verificarea unei conexiuni
- pentru trimiterea unei cereri sigure prin intermediul PDOS
- de rupere a conexiunii la baza de date
Primele 5 conțin gazda, numele bazei de date, datele de conectare, parola și codificarea și nu au nevoie de o explicație. În a 6-a proprietate, este util pentru noi să stocăm obiectul pdo. Primul pas este, desigur, de a scrie o metodă de conectare:
Prima linie conține un set de parametri pentru conectarea la server. Matricea $ connopt specifică diferitele moduri de lucru cu DOP. Aceste moduri pot fi folosite pentru a regla bug-urile și situațiile specifice. Nu vom intra în detaliile opțiunilor utilizate în acest manual. Oricine este interesat se poate referi la o documentație mai detaliată despre PHP. Aici observăm că în ultima linie vom crea un obiect pentru a lucra cu DOP, trecând parametrii specificați proiectantului și scrie acest obiect în proprietatea $ pdo.
Deci, ne-am conectat cu baza. Să scriem aceeași metodă pentru a verifica o conexiune. În acest scop, vom folosi metoda getAttribute (PDO :: ATTR_CONNECTION_STATUS). În cazul unei conexiuni normale, returnează șirul "hostname via TCP / IP", unde hostname este numele gazdei noastre.
Dacă proprietatea pdo este goală, atunci conexiunea nu este creată (sau a fost distrusă). Următorul pas este să verificați valoarea returnată a getAttribute și să o comparați cu valoarea normală. Dacă este diferit, aruncați-l la fals. Dacă totul a mers fără probleme - întoarce-te adevărat. Dacă nu, falsă.
Acum, de fapt, cele mai interesante. Să scriem o metodă pentru trimiterea unei cereri sigure la baza de date.
Metoda noastră va avea trei argumente:
- corpul cererii
- matrice asociativă cu un set de valori
- Un argument binar care specifică dacă trebuie returnate datele din baza de date. Pentru a executa SELECT'ov acest argument va fi adevărat, iar pentru INSERT'ov \ UPDATE'ov - false.
Să vedem ce se întâmplă aici. Prima condiție este necesară pentru a încerca să executați o interogare numai dacă există o conexiune la baza de date. În a doua condiție, verificăm o serie de valori (substituenți). Dacă matricea nu este specificată, executăm interogarea "așa cum este". Dacă matricea de substituenți există, o transmitem ca o argumentare metodei execute (). Metodele pregătesc () și execută () - și există ceva pentru care totul a fost creat. Așa cum ați fi observat, atunci când lucrați cu baza de date prin DOP, corpul solicitării și valorile sale sunt transmise separat unele de altele. În acest caz, solicitarea este scrisă în următoarea formă:
Unde: user_id este numele cheii din matricea trecută să execute (). Asta este, trimiterea explicită a unei solicitări folosind PDOStatement arată astfel:
Pentru a distruge o conexiune, trebuie doar să atribuiți un nul obiectului pdo. Prin urmare, metoda de eliminare a conexiunii va fi cea mai scurtă:
Deci, vom pune împreună clasa noastră: