Ghidul Securitatii PHP: Baze de date si SQL
< InapoiInainte >SesiuniProcesarea formurilor
Credentiale de acces expuse
Majoritatea aplicatiilor PHP interactioneaza cu o baza de date. Aceasta presupune conectarea la serverul bazei de date folosind credentialele de acces pentru autentificare:
<?php $host = 'example.org'; $username = 'myuser'; $password = 'mypass'; $db = mysql_connect($host, $username, $password); ?>
Acesta poate fi un exemplu de fisier numit db.inc care este inclus oricand e nevoie de conectare la baza de date. Aceasta abordare este convenabila, si tine datele de acces intr-un singur fisier.
Probleme pot aparea cand acest fisier este undeva in document root. Aceasta este o abordare folosita deseori, pentru ca face include si require mai usor de folosit, dar poate crea situatii in care credentialele de acces sunt expuse.
Tot ce este in document root are un URI asociat. Spre exemplu, daca document root este /usr/local/apache/htdocs, atunci fisierul /usr/local/apache/htdocs/inc/db.inc are un URI de genul http://example.org/inc/db.inc.
Combina acest lucru cu faptul ca majoritatea serverelor servesc fisiere .inc ca plaintext, si riscul de expunere a credentialelor devine clar. O problema este si ca sursa (codul) acestor module poate fi expusa, dar credentialele sunt informatii care trebuie neaparat protejate.
Desigur, o solutie simpla este sa tii toate modulele in afara document root-ului; aceasta este o abordare buna. Atat include cat si require accepta o cale din filesystem, deci nu e nevoie ca modulele sa fie accesibile via URI. Este un risc care nu e ncesar.
Daca nu ai de ales si modulele de acces trebuiesc plasate in document root, poti pune in fisierul httpd.conf (presupunanad ca folosesti Apache) ceva de genul:
<Files ~ "\.inc$">
Order allow,deny
Deny from all
</Files>
Nu e o idee buna ca modulele sa fie procesate de motorul PHP, prin renumirea modulelor cu extensia .php sau folosind AddType pentru a trata fisierele .inc ca fisiere PHP. Executarea codului scos din context poate fi foarte periculoasa, pentru ca este un lucru neasteptat si poate avea rezultate neasteptate. Totusi, daca modulele doar definesc variabile (spre exemplu), acest risc este redus.
Metoda mea favorita pentru a proteja credentialele de acces pentru baza de date este descrisa in PHP Cookbook (O'Reilly) de David Sklar si Adam Trachtenberg. Creaza un fisier, /path/to/secret-stuff, pe care numai root poate sa-l citeasca (nu nobody):
SetEnv DB_USER "myuser" SetEnv DB_PASS "mypass"
Include acest fisier in httpd.conf dupa cum urmeaza:
Include "/path/to/secret-stuff"
Acum poti folosi $_SERVER['DB_USER'] si $_SERVER['DB_PASS'] in cod. Nu va mai trebui sa scrii parola in nici un script, si web serverul nu poate citi fisierul secret-stuff, deci alti utilizatori nu pot scrie scripturi care sa acceseze credentialele de acces (indiferent de limbaj). Doar fii atent sa nu expui variabilele cu ceva de genul phpinfo() sau print_r($_SERVER).
SQL Injection
Este extrem de usor sa te aperi impotriva atacurilor ce se bazeaza pe SQL injection, dar multe aplicatii sunt totusi vulnerabile. Sa consideram urmatorul statement SQL:
<?php
$sql = "INSERT
INTO users (reg_username,
reg_password,
reg_email)
VALUES ('{$_POST['reg_username']}',
'$reg_password',
'{$_POST['reg_email']}')";
?>
Acesta este un query construit cu $_POST, ceea ce pare deja suspicios.
Sa presupunem ca acest query creaza un nou cont. Utilizatorul trimite un nume de utilizator si un email. Aplicatia genereaza o parola temporara si o trimite pe mailul utilizatorului. Sa ne imaginam ca utilizatorul trimite urmatorul username:
bad_guy', 'mypass', ''), ('good_guy
Acesta, in mod sigur, nu e un nume de utilizator valid, dar aplicatia nu stie pentru ca nu are implementat un sistem de filtrare. Daca o adresa de mail valida este introdusa (shiflett@php.net, spre exemplu), si 1234 este ceea ce aplicatia genereaza pentru parola, query-ul devine:
<?php
$sql = "INSERT
INTO users (reg_username,
reg_password,
reg_email)
VALUES ('bad_guy', 'mypass', ''), ('good_guy',
'1234',
'shiflett@php.net')"; ?>
In loc de a crea un cont (good_guy) cu o adresa de mail valida, aplicatia a fost pacalita si a creat doua conturi, iar utilizatorul a introdus toate detaliile pentru contul bad_guy.
In timp ce acest exemplu nu pare foarte periculos, este clar ca lucruri mai rele se pot intampla daca un atacator face modificari in query-urile SQL.
Spre exemplu, in functie de tipul bazei de date, poate fi posibil sa trimiti query-uri multiple intr-un singur statement. Astfel, atacatorul poate termina query-ul cu punct si virgula si apoi poate introduce orice query-uri doreste.
MySQL, pana de curand, nu permitea query-uri multiple astflel incat acest risc nu exista. Ultimele versiuni MySQL permit query-uri multiple, dar extensia PHP (ext/mysqli) te obliga sa folosesti o functie separata daca vrei sa trimiti query-uri multiple (mysqli_multi_query() in loc de mysqli_query()). E mai sigur daca un singur query este permis, pentru ca limiteaza ce rele poate face atacatorul.
E usor sa protejezi aplicatia de SQL injection:
-
Filtreaza toate datele.
O spun inca o data. Cu filtrare buna, majoritatea riscurilor de securitate sunt reduse, si unele sunt practic eliminate.
-
Citeaza datele.
Daca baza de date iti permite, (MySQL permite), pune toate variabilele din query in ghilimele simple, indiferent de genul de date pe care il contin.
-
Foloseste o functie pentru escape.
Uneori date valide pot schimba formatul statementului SQL. Foloseste mysql_escape_string() sau o functie de escaping pentru baza de date care o folosesti. Daca nu exista o functie specifica bazei tale de date, addslashes() este o buna ultima alternativa.
< InapoiInainte >SesiuniProcesarea formurilor