SQL INJECTION ATTACK PREVENTION în PHP | CODE EXAMPLES

după câțiva ani de testare a penetrării și de lucru cu echipele de dezvoltare pentru a-și asigura sistemele, am observat că nu a existat o postare completă pe blog care să ofere o explicație detaliată a modului de prevenire a atacurilor SQL injection în toate cele mai populare limbaje și cadre de dezvoltare a aplicațiilor web. Prin urmare, am decis să scriu o serie de postări multiple pentru a-l face cel mai complet ghid pentru prevenirea injecției SQL.

acest ghid este o serie deschisă de postări și acesta este primul post. Dacă doriți să adăugați orice tehnologie, vă rugăm să comentați mai jos și voi fi foarte fericit să explic prevenirea injecției SQL pentru acea tehnologie. Acest ghid va analiza următoarele tehnologii:

  • PHP
    • Symfony
    • CodeIgniter
    • CakePHP
    • FuelPHP
    • Zend
  • JEE
    • JDBC
    • JPA
  • ASP.NET
    • ADO.NET
    • ADO.Net Dopper
    • NHibernate

această primă postare se va concentra pe limbajul PHP și pe toate cadrele sale. Vor urma mai multe articole pentru a trata celelalte tehnologii enumerate.

pentru cei care nu știu ce este SQL injection attack, permiteți-mi să fac o mică introducere pentru a le explica acest lucru. Atacurile SQL injection se întâmplă atunci când un utilizator rău încearcă să injecteze o cerere SQL rău intenționată într-o cerere legitimă. Impactul atacului de injecție SQL diferă de la o situație la alta, în funcție de mai multe elemente legate de mediul aplicației și poate trece de la “simplu” ca scurgerea de informații la un control complet al serverului. Nu voi merge mai adânc în modul în care un atacator ar putea exploata această vulnerabilitate sau cum o putem descoperi în testele blackbox, deoarece acesta nu este obiectivul acestui post. Ceea ce vom vedea este cum să-l descoperim în codul sursă și cum să-l remediem.

cum să preveniți atacul de injecție SQL într-un cod sursă PHP pur ?

să începem călătoria noastră cu una dintre cele mai vechi și populare tehnologii de dezvoltare PHP fără cadre. Acum, primul lucru de făcut este să știți cum arată o linie de cod sursă vulnerabilă pentru a putea identifica vulnerabilitatea.

Iată un exemplu de solicitare de autentificare:

$request = "SELECT * FROM users WHERE username=". $_POST ." AND password = ". $_POST;
$result = $mysqli->query($request);

acum, dacă aruncați o privire atentă la cerere, veți observa că parametrii utilizatorului $_POST și $_POST sunt injectați direct în cererea SQL fără filtrare. Făcând acest lucru, faceți această linie de cod vulnerabilă la injecția SQL.

pentru a remedia această vulnerabilitate, va trebui să filtrați datele înainte de a le utiliza în cererea sql. Pentru a face acest lucru, cea mai bună soluție este să folosiți declarațiile pregătite. Iată un exemplu despre cum puteți utiliza declarațiile pregătite pentru a remedia această vulnerabilitate:

$conn = new mysqli($servername, $username, $password, $dbname);
$stmt = $conn->prepare("SELECT * FROM users WHERE username=? AND password=?");
$stmt->bind_param($username, $password);
$stmt->execute();

bună practică

aveți posibilitatea să nu utilizați funcția bind_param () aici este un exemplu de cum se face acest lucru :

$conn = new mysqli($servername, $username, $password, $dbname);
$stmt = $conn->prepare("SELECT * FROM users WHERE username=:username AND password=:password");
$stmt-> execute(array('username'=>$_POST,'password'=>$_POST));

după ani de analiză a codurilor sursă … am descoperit o eroare interesantă pe care mulți dezvoltatori o fac în timp ce remediază această vulnerabilitate. Aruncați o privire la următorul exemplu:

$stmt = $conn->prepare("SELECT * FROM users WHERE username=".$_POST." AND password=".$_POST);

dacă vă uitați la acest exemplu, veți vedea că folosim declarațiile pregătite. Cu toate acestea, parametrii sunt injectați direct în cererea SQL. Aceasta este o greșeală foarte frecventă pe care o fac dezvoltatorii și face codul vulnerabil la injecția SQL chiar dacă folosim declarațiile pregătite.

ideea din spatele declarațiilor pregătite este de a filtra parametrii înainte de a le injecta în cererea SQL … păstrați acest lucru în minte.

notă foarte importantă:

PHP oferă alte funcții pentru filtrarea cererilor SQL și prevenirea injecției SQL, dar unele dintre ele nu sunt eficiente. Funcția mysql_real_escape_string (), de exemplu, este una dintre cele mai cunoscute funcții PHP pentru a preveni injectarea SQL. Din păcate, această funcție este cu adevărat eficientă. Utilizând această funcție, Biblioteca MySQL va adăuga un backslash la următoarele caractere: NULL, \ x00, \ n, \ r,\,’, ” și \ x1a. cu toate acestea, pentru o situație ca aceasta :

$id = mysql_real_escape_string("1 OR 1=1");
$request = "SELECT * FROM users WHERE id = $id";

în culoarea roșie este ceea ce un utilizator rău vă va trimite

mysql_real_escape_string() nu vă va proteja împotriva unui astfel de atac.

acesta este motivul pentru care recomand întotdeauna utilizarea declarațiilor pregătite în locul acestei funcții.

cum să preveniți atacul de injecție SQL în cadrul Laravel ?

acum că am văzut atacul SQL injection împotriva unui cod PHP pur, acum este timpul să vedem cum putem remedia vulnerabilitatea într-o aplicație web bazată pe cadru. Înainte de a începe să vorbim despre acest lucru, permiteți-mi să vă dau mai întâi o mică descriere a cadrului Laravel.

Laravel este un cadru web open-source scris în PHP respectând principiul model-view-controller și dezvoltat în întregime în programarea orientată pe obiecte.

Exemplul 1

Iată un exemplu de cerere SQL vulnerabilă într-un laravel :

$user = DB::select('select * from users where username='. $request->post('username').' AND password='. $request->post('password'));

cadrul Laravel oferă câteva funcții minunate pentru a ajuta dezvoltatorii să asigure cererea SQL împotriva injecțiilor rău intenționate. Din păcate, majoritatea vulnerabilităților SQL injection pe care le descopăr în astfel de aplicații sunt legate de utilizarea necorespunzătoare a funcțiilor Laravel.

Să analizăm exemplul anterior, conform documentației Laravel funcția DB::select ar putea primi doi parametri. Primul este cererea SQL, iar al doilea este parametrii. Deci, pentru a filtra parametrii pentru SQL injection, va trebui să introduceți parametrii în cerere folosind al doilea parametru la fel ca acest exemplu:

$user = DB::select(‘select * de la users where username=? Și parola=?’, );

Exemplul 2

Laravel are mai multe moduri de a comunica cu baza de date în acest exemplu veți vedea că laravel forțează dezvoltatorul să utilizeze unele funcții care filtrează automat datele utilizatorului, cum ar fi următorul exemplu:

Users::where('username', $request->get('username'))->orderBy($request->get('orderby'))->get();

această funcție ar trebui să fie asigurată împotriva SQL injection și sunt, singura problemă este la nivelul funcției orderBy (). Conform documentației Laravel, această funcție nu filtrează datele utilizatorului, astfel încât un atac de injecție SQL este încă posibil prin această funcție.

cel mai bun lucru de făcut este să nu oferiți utilizatorului posibilitatea de a controla numele tabelului, dar dacă acest lucru este inevitabil, atunci va trebui să utilizați o listă albă pentru a valida datele utilizatorului înainte de a le introduce în acea funcție.

cum să preveniți atacul de injecție SQL în Symfony ?

să vedem un alt cadru PHP bine-cunoscut care oferă un set de componente PHP reutilizabile pentru a accelera dezvoltarea aplicațiilor PHP. Symfony este, de asemenea, utilizat de unele dintre cele mai cunoscute CMS web precum Drupal și Magento.

Symfony este unul dintre cele mai sigure cadre cu care puteți lucra, oferă un număr mare de funcții pentru a scrie un cod securizat. Cu toate acestea, dacă aceste funcții nu sunt bine utilizate, atunci veți obține un cod vulnerabil. Iată un exemplu de cod vulnerabil:

$entityManager = $this->getEntityManager();
$query = $entityManager->createQuery('SELECT p
FROM App\Entity\Product p
WHERE p.price > '. $request->query->get('price')
);

acum să analizăm acest cod vulnerabil. Conform documentației Symfony, createQuery () este o funcție care utilizează în mod implicit declarațiile pregătite. Deci, obiectul rezultat dintr-o astfel de funcție vă oferă acces la funcția setParameter (). Acesta filtrează toate datele utilizatorului pentru a evita o injecție SQL. Iată un exemplu de utilizare a acestuia :

$entityManager = $this->getEntityManager();
$query = $entityManager->createQuery('SELECT p
FROM App\Entity\Product p
WHERE p.price > :price
ORDER BY p.price ASC'
)->setParameter('price', $price);

Cum de a preveni atacul de injecție SQL în Codeigniter ?

CodeIgniter unul dintre cele mai puternice, ușoare și populare cadre PHP cu o amprentă foarte mică. Acesta a fost construit pentru dezvoltatorii care au nevoie de un set de instrumente simplu și elegant pentru a crea aplicații web full-featured. La fel ca Symfony și Laravel, Codeigniter vine și cu unele sisteme de securitate pentru a ajuta dezvoltatorii să creeze aplicații mai sigure.

cu toate acestea, și cu Codeigniter unii dezvoltatori fac aceeași greșeală și injectează datele utilizatorului fără filtrare. Iată un exemplu de astfel de erori care duc la un atac de injecție SQL:

$query = 'SELECT * FROM users WHERE username = '. $this-> input->post('username'). ' AND password= '. $this-> input->post('password');
$this->db->query($query);

acum, pentru a remedia această problemă, va trebui să aruncați o privire la documentația Codeigniter. Potrivit acestuia, funcția query() ia doi parametri. Primul este interogarea sql, iar al doilea este parametrii pe care doriți să le legați.

Exemplul 1

în mod implicit, această funcție filtrează toți parametrii legați pentru a preveni injecțiile SQL. Iată un exemplu de mod corect de a utiliza această funcție:

$query = 'SELECT * FROM users WHERE username = ? AND password = ? ';
$this->db->query($query, array($this->input->post('username'), $this-> input-> post('password')));

Exemplul 2:

Codeigniter oferă un alt mod de a efectua cererea SQL va preveni injecțiile SQL. Iată un exemplu de utilizare a clasei Active Record pentru a preveni injecțiile SQL:

$this->db->get_where('users',array('username'=>$this->input->post('username') ,'password' => $this->input->post('password')));

get_where () funcția nu mai există în versiunea 4 a Codeigniter.

Notă 1

Codeigniter oferă, de asemenea, o funcție care arată ca, mysql_real_escape_string() funcție numită escape(). Dar, așa cum am spus mai întâi mysql_real_escape_string() ar putea fi ocolit în unele cazuri și trebuie să evitați utilizarea acestuia. Faptul că funcția escape () face exact același lucru, atunci ar fi posibil să-l ocolească, de asemenea. Acesta este motivul pentru care nu încurajez dezvoltatorii să utilizeze funcția escape ().

Notă 2

cum să preveniți atacul de injecție SQL în CakePHP ?

CakePHP este un cadru de dezvoltare rapidă pentru PHP care utilizează modele de design cunoscute, cum ar fi maparea asociativă a datelor, controlerul frontal și MVC. CakePHP oferă o grămadă de componente încorporate pentru a facilita dezvoltarea aplicațiilor web și le va face mai sigure.

cu toate acestea, aplicațiile CakePHP sunt, de asemenea, vulnerabile la injecțiile SQL dacă nu sunt bine utilizate. Iată un exemplu de cod vulnerabil la atacul de injecție SQL în acest cadru:

$results = $connection-> execute('SELECT * FROM users WHERE username = '. $this->request->getParam('username').' AND password='. $this->request->getParam('password'))->fetchAll('assoc');

funcția execute () utilizează în mod implicit instrucțiunile pregătite. Cu toate acestea, exemplul anterior încă vulnerabile la atac SQL injection, ca datele de utilizator sunt inserate direct într-o cerere sql legitim. Iată un exemplu al modului corect de a utiliza această funcție:

$results = $connection->execute('SELECT * FROM users WHERE username = :username AND password=:password', )->fetchAll('assoc');

cadrul CakePHP oferă, de asemenea, un sistem numit Query Builder, care forțează filtrarea datelor utilizatorului pentru a preveni atacurile de injecție SQL. Conform documentației CakePHP, sub capace, Query Builder utilizează declarațiile pregătite.

aici este un exemplu de modul în care puteți utiliza un astfel de sistem în mod corect:

use Cake\ORM\Locator\LocatorAwareTrait;
$users = $this->getTableLocator()->get('users');
// Start a new query.
$query = $users->find();
$query->where();

cum să preveniți atacul de injecție SQL în FuelPHP ?

FuelPHP este unul dintre cele mai recente framework PHP care s-a născut pe baza celor mai bune idei ale fiecărui framework de pe piață. A fost dezvoltat cu PHP 5 și este complet orientat pe obiecte. În FuelPHP, securitatea a fost Frontul și Centrul de îngrijorare, ceea ce i-a împins pe colaboratorii săi să implementeze multe mecanisme de securitate pentru a filtra datele utilizatorilor. SQL injection atacă unul dintre motivele pentru implementarea unor astfel de mecanisme în FuelPHP.

cu toate acestea, chiar și cu un cadru atât de puternic, o simplă utilizare greșită a acestor mecanisme a pus întregul cod în pericol pentru un atac de injecție SQL. Iată un exemplu de astfel de eroare de cod:

$query = "SELECT * FROM article WHERE id = ". Input::get('id');
$result = DB::query($query)->execute();

pentru a remedia acest lucru, există, de asemenea, două tehnici precum celelalte cadre. Primul este de a utiliza corect funcția query (), prin legarea parametrilor, cum ar fi următorul exemplu :

$query = "SELECT * FROM article WHERE id = :id"; // our query
$result = DB::query($query)->bind('id', Input::get('id'))->execute();

a doua soluție este utilizarea mecanismului ORM implementat în cadrul FuelPHP pentru a comunica cu baza de date. Iată un exemplu de utilizare a acestuia:

$user = DB::select()->from('article')->where('id', Input::get('id'))->execute();

cum să preveniți atacul de injecție SQL în zend framework ?

Zend framework (schimbat în proiectul Laminas) este unul dintre cele mai cunoscute cadre din lumea PHP. A fost dezvoltat având în vedere reglarea performanței, ceea ce explică de ce fiecare versiune nouă este mult mai rapidă decât cea veche. Zend a fost, de asemenea, dezvoltat cu cele mai bune practici de securitate, care i-au împins pe dezvoltatorii săi să implementeze mecanisme de securitate suplimentare pentru a face față amenințărilor cibernetice cunoscute.

unele dintre aceste mecanisme au fost implementate pentru a face față atacurilor de injecție SQL. Dar, ca întotdeauna, o utilizare greșită a acestor mecanisme duce la un cod vulnerabil. În această parte a articolului voi arăta un exemplu de astfel de eroare:

$adapter->query('SELECT * FROM `article` WHERE `id` = '. $this->getRequest()->getPost('id'));

Iată cum să remediați această vulnerabilitate:

$adapter->query('SELECT * FROM `article` WHERE `id` = ?', );

Leave a Reply