PHP
downloads | documentation | faq | getting help | mailing lists | wiki | reporting bugs | php.net sites | links | conferences | my php.net

search for in the

Error Reporting> <Securitatea Bazelor de Date
Last updated: Fri, 28 Nov 2008

view this page in

Injectarea SQL

Mulţi developeri web nu ştiu cum pot fi manipulate interpelările SQL, şi acordă toata încrederea unei asemenea comenzi. Interpelările SQL pot ocoli controalele de acces, în consecinţă să treacă peste metodele de autentificare şi verificările de autorizaţie, iar câteodată pot chiar să faciliteze accesul la comenzile de sistem.

Injectarea directă a comenzilor SQL este o tehnică în care atacatorul creează sau modifică comenzile SQL pentru a scoate la iveală datele sensibile, sau pentru a suprascrie o anumită valoare, sau chiar pentru a executa comenzi periculoase la nivel de sistem. Acest lucru este înfaptuit de către aplicaţia care preia inputul utilizatorului, îl combină cu parametrii statici pentru a forma o interpelare SQL. Următoarele exemple sunt bazate pe cazuri reale, cu regret.

Datorită lipsei validării inputului şi conectării la baza de date cu drepturi de superuser, sau a unui user care poate crea la rândul lui alţi useri, atacatorul poate crea un superuser în baza de date.

Example #1 Împărţirea rezultatelor în mai multe pagini ... şi crearea de superuseri (PostgreSQL)

<?php

$offset 
argv[0]; // atentie, nu se face validarea inputului!
$query  "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
$result pg_query($conn$query);

?>
Utilizatorii obişnuiţi fac click pe linkurile 'next', 'prev' unde variabila $offset este encodată în URL. Scriptul se aşteaptă ca variabila $offset să fie un număr zecimal. Cu toate acestea, ce se întâmplă dacă cineva încearcă să intervină, adăugând la URL următoarele date prelucrate cu funcţia urlencode():
0;
insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
    select 'crack', usesysid, 't','t','crack'
    from pg_shadow where usename='postgres';
--
Dacă se execută, atunci scriptul va permite modificarea parolei superuser-ului. Observaţi că 0; este pentru a oferi un offset corect interpelării originale şi pentru a o termina.

Notă: Este o tehnică obişnuită de a forţa parserul SQL să ignore restul interpelării scrise de developer, cu ajutorul --, care este simbolul pentru comentariu în SQL.

O reală posibilitate de a afla parole este de a manipula rezultatele din paginile de căutare. Singurul lucru de care are nevoie atacatorul este să vadă dacă există variabile în declaraţiile SQL care nu sunt protejate corespunzător. Se pot manipula variabilele din formularele care utilizează WHERE, ORDER BY, LIMIT sau condiţiile OFFSET din declaraţiile SELECT. Dacă baza de date suportă construcţii UNION, atacatorul poate încerca să lipească o interpelare întreagă la cea originală pentru a lista parolele dintr-un tabel arbitrar. Folosirea parolelor criptate este pe deplin încurajată.

Example #2 Listarea unor articole ... şi a unor parole (orice server de baze de date)

<?php

$query  
"SELECT id, name, inserted, size FROM products
                  WHERE size = '$size'
                  ORDER BY $order LIMIT $limit, $offset;"
;
$result odbc_exec($conn$query);

?>
Partea statică a interpelării poate fi combinată cu înca un SELECT care să arate parolele:
'
union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable;
--
Dacă această interpelare (ne-am jucat cu ' şi --) ar fi fost atribuită unei variabile utilizate la formarea $query, am fi dat de belea.

Comanda SQL UPDATE nu este nici ea ocolită de probleme. Aceste interpelări sunt ameninţate de atacurile prin tăierea şi alipirea unei noi interpelări. În plus, atacatorul se mai poate juca şi cu declaraţia SET. În acest caz, atacatorul trebuie să cunoască careva informaţii despre schemă, de ex. structura tabelului din care doreşte să extragă sau să manipuleze informaţia. Acest lucru poate fi facut prin examinarea denumirilor variabilelor din formulare, sau prin procedeul brute-force. Nu există multe convenţii prin care se delimitează câmpurile pentru user sau parolă.

Example #3 De la resetarea unei parole ... până la obţinerea mai multor privilegii (orice server de baze de date)

<?php
$query 
"UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";
?>
Însă un utilizator rău voit poate introduce ' or uid like'%admin%'; -- în locul $uid pentru a schimba parola utilizatorului admin, sau pur şi simplu setează valoarea $pwd în "hehehe', admin='yes', trusted=100 " (cu spaţiu la sfârşit) pentru a obţine mai multe privilegii. În acest caz interpelarea ar arăta în felul următor:
<?php

// $uid == ' or uid like'%admin%'; --
$query "UPDATE usertable SET pwd='...' WHERE uid='' or uid like '%admin%'; --";

// $pwd == "hehehe', admin='yes', trusted=100 "
$query "UPDATE usertable SET pwd='hehehe', admin='yes', trusted=100 WHERE
...;"
;

?>

Un exemplu înspăimântător despre cum pot fi rulate comenzi la nivel de sistem de operare pe unele servere de baze de date.

Example #4 Atacarea sistemului de operare pe care lucrează baza de date (MSSQL Server)

<?php

$query  
"SELECT * FROM products WHERE id LIKE '%$prod%'";
$result mssql_query($query);

?>
Dacă un atacator introduce valoarea a%' exec master..xp_cmdshell 'net user test testpass /ADD' -- în loc de $prod, atunci $query va deveni:
<?php

$query  
"SELECT * FROM products
                    WHERE id LIKE '%a%'
                    exec master..xp_cmdshell 'net user test testpass /ADD'--"
;
$result mssql_query($query);

?>
Serverul MSSQL execută interpelarea SQL incluzând şi comanda de adăugare a unui user nou în baza de date cu conturi locale. Dacă această aplicaţie rula ca sa şi serviciul MSSQLSERVER rula cu destule privilegii, atacatorul avea acum un cont pe serverul respectiv unde să execute comenzi.

Notă: Unele dintre exemplele de mai sus sunt legate de anumite servere de baze de date. Acest lucru nu înseamnă că atacuri similare nu pot avea loc asupra altor produse similare lor. Serverul dumneavoastră de baze de date poate fi vulnerabil într-o manieră asemănătoare.

Tehnici de evitare

Puteţi spune că un atacator trebuie să deţină informaţii despre baza de date şi schema acesteia în majoritatea exemplelor. În majoritatea cazurilor aşa este, dar nu se ştie niciodată cum poate fi descoperită aceasta, în mod direct sau indirect. Dacă folosiţi un soft open source, sau alt pachet disponibil publicului larg (content management system sau forum), atacatorii pot duplica cu uşurinţă codul dumneavoastră. De asemenea un risc îl reprezintă şi designul necorespunzător al bazei de date.

Aceste atacuri sunt de obicei bazate pe exploatarea codului scris de developeri fără a lua în calcul securitatea lui. Niciodată nu aveţi încredere în nici un fel de input, mai ales când acesta provine din partea clientului, chiar dacă acesta vine dintr-un câmp select, câmp ascuns sau cookie. Primul exemplu arată că o interpelare aparent nevinovată poate cauza un dezastru.

  • Niciodată nu vă conectaţi la baza de date ca superuser sau ca orice alt utilizator care poate manipula mai multe baze de date decât cea folosită. Folosiţi întotdeauna useri cu privilegii limitate.
  • Verificaţi dacă un input conţine tipul de date corect. PHP are o varietate de funcţii de validare, de la cele mai simple, care pot fi găsite în Funcţii asupra variabilelor şi în Funcţii ale tipurilor de caractere (de ex. is_numeric(), ctype_digit() respectiv) şi până la Expresii regulate compatibile Perl.
  • Dacă aplicaţia aşteaptă un input numeric, încercaţi să verificaţi datele cu funcţia is_numeric(), sau schimbaţi tipul variabilei utilizând funcţia settype(), sau folosiţi reprezentaţia numerică prin sprintf().

    Example #5 O metodă mai sigură de formare a interpelării pentru paginare

    <?php

    settype
    ($offset'integer');
    $query "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";

    // observaţi %d (care înseamnă formatul integer) din string-ul de formatare,
    // folosirea %s (string) ar fi fără sens
    $query sprintf("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;",
                     
    $offset);

    ?>

  • Aplicaţi asupra fiecărei valori non-numerice furnizate de utilizator, ce va fi transmisă bazei de date, funcţia 'string escape' specifică fiecărei baze de date (de ex. mysql_real_escape_string(), sql_escape_string(), etc.). Dacă baza de date nu posedă un mecanism specific de 'string escape', pot fi utile funcţiile addslashes() şi str_replace() (în dependenţă de tipul bazei de date). Vedeţi primul exemplu. Dupa cum arată exemplul, adăugarea ghilimelelor în partea statică a interpelării nu este de ajuns, făcând-o susceptibilă la atacuri.
  • Nu afişaţi informaţii specifice bazei de date, în special despre schema acesteia. Vedeţi de asemenea Raportarea erorilor şi Prelucrarea erorilor şi funcţii de logare.
  • Puteţi utiliza proceduri stocate şi cursoare de date predefinite pentru a abstractiza accesul la date, pentru ca utilizatorii să nu interacţioneze direct cu tabelele sau viziunile, dar această soluţie poate avea alte consecinţe.

În afară de acestea, puteţi loga interpelările în interiorul scriptului şi în baza de date, dacă aceasta susţine acest lucru. Bineînţeles, logarea nu poate preveni atacurile sau încercările de a vătăma baza de date, dar poate fi utilă în depistarea aplicaţiei în care a avut loc incidentul. Log-ul nu este util prin sine, ci prin informaţia pe care o conţine. Mai multă detaliere este de obicei mai bună decât lipsa detaliilor.



Error Reporting> <Securitatea Bazelor de Date
Last updated: Fri, 28 Nov 2008
 
add a note add a note User Contributed Notes
Injectarea SQL
fOV
12-Nov-2008 02:53
The best prevention is to deactivate master..xp_cmdshell.
Run in isql the command `sp_configure 'xp_cmdshell''
If the value for "config value" is 1 then make in the isql
`sp_configure 'xp_cmdshell',0'.
If you want see the options from your mssql-Server make
`sp_configure 'show advanced options',1'
and then `sp_configure'
jaimthorn at yahoo dot com
13-Oct-2008 12:32
dark dot avenger at email dot cz wrote:

"I think that easy way to protect against SQL injection is to convert inputted data into binary format, so that whatever input is, in sql query it will consist only of 1s and 0s."

Unless there is a 1-to-1 correspondence between your inputted data and the characters in your 'binary' format, a SELECT query wouldn't work anymore.  Not a binary format, but it makes my point: MIME encoding the text 'Dark Avenger' results in 'RGFyayBBdmVuZ2Vy'.  If I wanted to look up anyone with 'Avenger' in his/her name, then 'Avenger' would be encoded as 'QXZlbmdlcg==' which clearly wouldn't result in a hit on 'RGFyayBBdmVuZ2Vy'.

If there IS a 1-to-1 correspondence, then EITHER your solution only makes it a bit harder to perform a SQL injection (a hacker would have to figure out what mapping was used between the text and the 'binary' format), OR you've come up with simply another way to escaping your data.  Either isn't a terribly good solution to the SQL injection problem.
dark dot avenger at email dot cz
14-Aug-2008 11:09
I think that easy way to protect against SQL injection is to convert inputted data into binary format, so that whatever input is, in sql query it will consist only of 1s and 0s.
valerylourie at gmail dot com
15-Apr-2008 10:23
Note that PHP 5 introduced filters that you can use for untrusted user input:
http://us.php.net/manual/en/intro.filter.php
ctm at etheon dot net
01-Aug-2006 06:39
This is a very helpful document from the MySQL site (in .pdf format) :

http://dev.mysql.com/tech-resources/articles/
guide-to-php-security-ch3.pdf
17-Mar-2006 07:48
If you use the PEAR package and prepare() / execute() your queries,
you will hardly have to worry about any of this. Of course, it's still
a good idea to make sure you're putting valid data in your database...
anonymous
25-Jan-2005 10:42
Here is a useful class that deals with SQL injection:

http://www.phpinsider.com/php/code/SafeSQL/

Error Reporting> <Securitatea Bazelor de Date
Last updated: Fri, 28 Nov 2008
 
 
show source | credits | sitemap | contact | advertising | mirror sites