cvs: phpdoc-ro /security database.xml
| From: | Serban Gh. Ghita | Date: | Mon, 23 Aug 2004 15:42:18 +0000 |
| Subject: | cvs: phpdoc-ro /security database.xml | ||
| Groups: | php.doc.ro | ||
| Request: | Send a blank email to doc-ro+get-106@lists.php.net to get a copy of this message | ||
zamolxe Mon Aug 23 11:42:18 2004 EDT
Added files:
/phpdoc-ro/security database.xml
Log:
Securitatea Bazelor de Date - traduse, adaptate, corectate 100%
http://cvs.php.net/co.php/phpdoc-ro/security/database.xml?r=1.1&p=1 Index: phpdoc-ro/security/database.xml +++ phpdoc-ro/security/database.xml <?xml version="1.0" encoding="iso-8859-1"?> <!-- $Revision: 1.1 $ --> <!-- splitted from ./index.xml, last change in rev 1.66 --> <chapter id="security.database"> <title>Securitatea Bazelor de Date</title> <simpara> In zilele noastre, bazele de date sunt componente esentiale ale aplicatiilor web, dand posibilitatea acestora de a servi continut dinamic. Datorita faptului ca informatii secrete sau cu caracter confidential se stocheaza adesea intr-o baza de date, va trebui sa luati in considerare protejarea bazelor de date. </simpara> <simpara> Pentru a primi sau trimite orice informatie, trebuie sa te conectezi la baza de date, sa trimiti un query valid, sa iei rezultatele, si sa inchizi conexiunea. Recent, cel mai folosit limbaj query pentru asemenea tip de interactiune este Nowadays, the commonly used query language in this interaction is the Structured Query Language (SQL). Vezi cum un atacator poate <link linkend="security.database.sql-injection">sa manipuleze un query SQL</link>. </simpara> <simpara> Dupa cum probabil ati presupus, PHP nu va poate proteja bazele de date in sine. Urmatoarele sectiuni tind sa fie o introducere in notiunile de baza ale accesarii si manipularii bazelor de date, cu scripturi PHP. </simpara> <simpara> Pastrati in minte urmatoarea regula: securitate maxima. Cu cat cresteti masurile de protectie asupra bazelor de date, cu atat mai putina este probabilitatea ca un atacator sa reuseasca sa sustraga date importante sau sa abuzeze de ele. Incercati ca structura si designul bazei de date sa fie cat mai eficient, dupa care va puteti pune problemele legate de securitate. </simpara> <sect1 id="security.database.design"> <title>Designul bazelor de date</title> <simpara> Primul pas este intotdeauna crearea bazei de date, numai in cazul in care nu doriti sa folositi alta de pe alt server. Cand o baza de date este creata, ea este desemnata unui anume utilizator, care a executat comanda de creare. In general numai proprietarul (sau superuserul) pot face orice cu obiectele din acea baza de date, iar pentru a lasa si alti utilizatori sa o foloseasca, ei trebuie sa aiba privilegii. </simpara> <simpara> Aplicatiile pe care le construiti nu ar trebui sa se conecteze niciodata la o baza de date cu privilegii de administrator sau superuser, pentru ca acesti utilizatori pot executa orice fel de query, de exemplu, modificarea schemei (stergerea tabelelor) sau stergerea intregului continut. </simpara> <simpara> Poti crea diferiti useri de baze de date pentru fiecare aspect al aplicatiei, cu drepturi limitate, in functie de task-urile pe care le indeplinesc. Se pot da numai privilegiile strict necesare, si evitati ca acelasi user sa interactioneze cu mai multe baze de date. Aceasta inseamna ca daca un intrus va avea privilegii la baza de date, acesta va avea privilegii limitate si nu va putea sa faca decat schimbari cu impact asupra aplicatie dumneavoastra. </simpara> <simpara> Este bine sa nu implementati toate solutiile prin prisma scriptului web, in schimb incercati sa creati o schema de baza de date cat mai competitiva, din care sa extrageti simplu informatia. Daca sistemul evolueaza, noi porturi vor fi folosite pentru a manipula bazele de date, si va trebui sa reimplementati scripturile pentru fiecare baza de date in parte. </simpara> </sect1> <sect1 id="security.database.connection"> <title>Conectarea la baza de date</title> <simpara> Se poate sa doriti sa va conectati la baza de date folosind encriptia SSL pentru a spori siguranta datelor, sau puteti folosi ssh pentru a cripta datele dintre clientii din retea si serverul de baze de date. Daca este folosita una dintre aceste metode atunci monitorizarea traficului si accesul la informatiile sensibile despre bazele de date ar fi foarte dificila pentru un atacator. </simpara> <!--simpara> If your database server has native SSL support, consider using <link linkend="ref.openssl">OpenSSL functions</link> in communication between PHP and database via SSL. </simpara--> </sect1> <sect1 id="security.database.storage"> <title>Model de stocare criptata</title> <simpara> SSL/SSH protejeaza traversarea datelor de la client la server, insa SSL/SSH nu protejeaza datele stocate in baza de date. SSL este un protocol on-the-wire adica are grija numai de informatia ce traverseaza traseul descris mai sus. </simpara> <simpara> Odata ce atacatorul castiga acces la baza de date direct (trecand peste serverul web), informatia stocata poate fi expusa sau abuzata, numai daca ea este protejata de insasi baza de date. Encriptia datelor este o buna masura pentru a diminua acest risc, dar prea putine baze de date oferta acest tip de encriptie. </simpara> <simpara> Cea mai usoara cale de a rezolva problema este de a crea propriul pachet de encriptie, si apoi folosirea lui cu scripturile PHP. PHP poate asigura urmatoarele extensii ca <link linkend="ref.mcrypt">Mcrypt</link> si <link linkend="ref.mhash">Mhash</link>, care acopera o varietate larga de algoritmi de encriptie. Scriptul cripteaza datele inainte de a fi inserate in baza de date, si le deripteaza la extragerea acestora. Verificati documentatia de rigoare pentru a studia mai multe despre modalitatile de operare ale encriptiei. </simpara> <simpara> In cazul datelor care trebuie sa fie confidentiale, a caror expunere nu este necesara in nici un context, procedeul hashing poate fi luat in considerare. Cel mai cunoscut exemplu este stocarea hash-ului MD5 a unei parole in baza de date, in loc de stocarea ei in format plain. Vezi de asemenea si functiile <function>crypt</function> and <function>md5</function>. </simpara> <example> <title>Utilizarea hash-ului pentru parole</title> <programlisting role="php"> <![CDATA[ <?php // stocam hash-ul parolei --> md5($password) $query = sprintf("INSERT INTO users(name,pwd) VALUES('%s','%s');", addslashes($username), md5($password)); $result = pg_exec($connection, $query); // query - verificare daca utilizatorul a introdus parola corecta $query = sprintf("SELECT 1 FROM users WHERE name='%s' AND pwd='%s';", addslashes($username), md5($password)); $result = pg_exec($connection, $query); if (pg_numrows($result) > 0) { echo "Welcome, $username!"; } else { echo "Authentication failed for $username."; } ?> ]]> </programlisting> </example> </sect1> <sect1 id="security.database.sql-injection"> <title>Injectia SQL</title> <simpara> Multi developeri web nu stiu cum pot fi manipulate query-urile SQL, si acorda toata increderea unei asemenea comenzi. Query-urile SQL pot ocoli controalele de acces, in consecinta sa treaca peste metodele de autentificare si verificarile de autorizatie, iar cateodata pot chiar sa faciliteze accesul la comenzile de sistem. </simpara> <simpara> Injectia directa prin comanda SQL este o tehnica in care atacatorul creaza sau modifica comenzile SQL pentru a scoate la iveala datele sensibile, sau pentru a suprascrie o anumita valoare, sau chiar pentru a executa comenzi periculoase la nivel de sistem. Acest lucru este infaptuit de catre aplicatia care preia inputul utilizatorului si combinata cu parametrii statici formeaza un query SQL. Urmatoarele exemple sunt bazate pe greseli italnite la web developeri. </simpara> <para> Datorita lipsei validarii inputului si conectarii la baza de date cu drepturi de superuser, sau a unui user care poate crea la randul lui alti useri, atacatorul poate crea un superuser in baza de date. <example> <title> Impartirea rezultatelor in mai multe pagini ... si crearea de superuser (PostgreSQL and MySQL) </title> <programlisting role="php"> <![CDATA[ <?php $offset = argv[0]; // atentie, nu se face validarea inputului! $query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;"; // cu PostgreSQL $result = pg_exec($conn, $query); // cu MySQL $result = mysql_query($query); ?> ]]> </programlisting> </example> Utilizatorii obisnuiti fac click pe linkurile 'next', 'prev' unde este variabila <varname>$offset</varname> encodata in URL. Scriptul se asteapta ca variabila <varname>$offset</varname> sa fie un numar decimal. Cu toate acestea, ce se intampla daca cineva incerca sa modifice URL-ul si s-ar folosi de <function>urlencode</function> pentru a insera urmatorul URL: <informalexample> <programlisting role="sql"> <![CDATA[ // in cazul PostgreSQL 0; insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd) select 'crack', usesysid, 't','t','crack' from pg_shadow where usename='postgres'; -- // in cazul MySQL 0; UPDATE user SET Password=PASSWORD('crack') WHERE user='root'; FLUSH PRIVILEGES; ]]> </programlisting> </informalexample> Daca se executa, atunci scriptul va schimba parola superuserului root. Observati ca <literal>0;</literal> este pentru a oferi un offset corect query-ului si pentru a-l termina. </para> <note> <para> Exista o tehnica de a forta parserul SQL sa ignore restul queryului scris de developer, cu ajutorul <literal>--</literal> care este simbolul pentru comentariu in SQL. </para> </note> <para> O reala posibilitate de a afla parole este de a manipula rezultatele din paginile de cautare. Singurul lucru de care are nevoie atacatorul este sa vada daca exista variabile in declaratiile SQL care nu sunt protejate corespunzator. Se pot manipula variabilele din formularele care utilizeaza <literal>WHERE, ORDER BY, LIMIT</literal> sau conditiile <literal>OFFSET</literal> din declaratiile <literal>SELECT</literal> Daca baza de date suporta <literal>UNION</literal>, atacatorul poate incerca sa lipeasca un query diferit la cel original pentru a lista parolele dintr-o tabela arbitrara. Folosirea parolelor criptate este pe deplin incurajata. <example> <title> Listarea unor articole ... si a unor parole (orice server de baze de date) </title> <programlisting role="php"> <![CDATA[ <?php $query = "SELECT id, name, inserted, size FROM products WHERE size = '$size' ORDER BY $order LIMIT $limit, $offset;"; $result = odbc_exec($conn, $query); ?> ]]> </programlisting> </example> Partea statica a queryului poate fi combinata cu inca un <literal>SELECT</literal> care sa arate parolele: <informalexample> <programlisting role="sql"> <![CDATA[ ' union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable; -- ]]> </programlisting> </informalexample> In acest query (ne-am jucat cu <literal>'</literal> si <literal>--</literal>) care au fost asignate variabilei <varname>$query</varname>, adica query-ului cu probleme. </para> <para> Comanda SQL UPDATE nu este nici ea ocolita de probleme. Aceste query-uri sunt amenintate de atacurile prin taierea si alipirea unui nou query. In plus, atacatorul se mai poate juca si cu declaratia <literal>SET</literal>. In acest caz, atacatorul trebuie sa cunoasca schema SQL a tabelului din care doreste sa extraga sau sa manipuleze informatia. Acest lucru poate fi facut prin examinarea codului sursa, deci a variabilelor unui formular, sau prin procedeul brute-force. Nu exista multe conventii prin care se delimiteaza campurile pentru user sau parola. <example> <title> Formular de resetare a parolei ... si de castigare a unor privilegii (orice baza de date) </title> <programlisting role="php"> <![CDATA[ <?php $query = "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';"; ?> ]]> </programlisting> </example> Dar un utilizator rau voit introduce <literal>' or uid like'%admin%'; --</literal> in locul <varname>$uid</varname> pentru a schimba parola utilizatorului admin, sau pentru a seta valoarea <varname>$pwd</varname> in <literal>"hehehe', admin='yes', trusted=100 "</literal> (cu spatiu la sfarsit) pentru a castiga mai multe privilegii. Apoi queryul ar arata in felul urmator: <informalexample> <programlisting role="php"> <![CDATA[ <?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....;"; ?> ]]> </programlisting> </informalexample> </para> <para> Un exemplu inspaimantator despre cum poti rula comenzi la nivel de sistem de operare pe unele servere de baze de date. <example> <title>Atacarea sistemului de operare pe care lucreaza baza de date (MSSQL Server)</title> <programlisting role="php"> <![CDATA[ <?php $query = "SELECT * FROM products WHERE id LIKE '%$prod%'"; $result = mssql_query($query); ?> ]]> </programlisting> </example> Daca un atacator introduce valoarea <literal>a%' exec master..xp_cmdshell 'net user test testpass /ADD' --</literal> in loc de <varname>$prod</varname>, atunci variabila <varname>$query</varname> va deveni: <informalexample> <programlisting role="php"> <![CDATA[ <?php $query = "SELECT * FROM products WHERE id LIKE '%a%' exec master..xp_cmdshell 'net user test testpass /ADD'--"; $result = mssql_query($query); ?> ]]> </programlisting> </informalexample> Serverul MSSQL executa declaratia SQL incluzand si comanda de adaugare de user nou in baza de date cu conturi locale. Daca aceasta aplicatie rula ca <literal>sa</literal> si serviciul MSSQLSERVER rula cu destule privilegii, atacatorul avea acum un cont pe serverul respectiv unde sa execute comenzi. </para> <note> <para> Unele dintre exemplele de mai sus sunt legate de animite baze de date. Acest lucru nu inseamna ca atacuri similare nu pot avea loc pe produse similare lor. Baza de date poate fi vulnerabila intr-o maniera asemanatoare, fiind exploatabila prin metode asemanatoare. </para> </note> <sect2 id="security.database.avoiding"> <title>Tehnici de evitare</title> <simpara> Puteti spune ca un atacator trebuie sa detina informatii despre baza de date si schema acesteia in majoritatea exemplelor. In maoritatea cazurilor asa este, dar nu se stie niciodata cum poate fi descoperita aceasta, in mod direct sau indirect. Daca folositi un soft open source, sau alt pachet disponibil publicului larg (content management system sau forum), atacatorii pot duplica cu usurinta codul dumneavoastra. De asemenea un risc il reprezinta si designul necorespunzator al bazei de date. </simpara> <simpara> Aceste atacuri sunt de obicei bazate pe exploatarea codului scris de developeri fara a lua in calcul securitatea lui. Niciodata nu aveti incredere in nici un fel de input, mai ales cand acesta provine de la partea client side, sau camp select, camp ascuns sau cookie.Primul exemplu arata ca asemenea scapari pot duce la query-uri dezastruoase. </simpara> <itemizedlist> <listitem> <simpara> Niciodata nu va conectati la baza de date ca superuser sau ca orice alt utilizator care poate manipula mai multe baze de date decat cea folosita. Folositi intotdeauna useri cu privilegii limitate. </simpara> </listitem> <listitem> <simpara> Verificati daca un input contine tipul de date corect. PHP are o varietate de functii de validare, de la cele mai simple, care pot fi gasite in <link linkend="ref.var">Variable Functions</link> si in <link linkend="ref.ctype">Character Type Functions</link> (e.x. <function>is_numeric</function>, <function>ctype_digit</function>) si pana la <link linkend="ref.pcre">Expresiile regulate compatibile Perl</link>. </simpara> </listitem> <listitem> <para> Daca aplicatia asteapta un input numeric, incercati sa verificati datele cu functia <function>is_numeric</function>, sau schimbati tipul variabilei utilizand functia <function>settype</function>, sau folositi reprezentatia numerica prin <function>sprintf</function>. <example> <title>O metoda mai sigura metoda de query pentru paginare</title> <programlisting role="php"> <![CDATA[ <?php settype($offset, 'integer'); $query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;"; // observati %d din query care inseamna formatul integer, folosirea %s (string) ar fi fara sens $query = sprintf("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;", $offset); ?> ]]> </programlisting> </example> </para> </listitem> <listitem> <simpara> Adauga functia <function>addslashes</function> sau <function>addcslashes</function> fiecarui input care nu este numeric. Vezi <link linkend="security.database.storage">primul exemplu</link>. Dupa cum arata exemplul, eliminarea ghilimelelor in partea statica a query-ului, nu este de ajuns. </simpara> </listitem> <listitem> <simpara> Nu afisati informatii specifice bazei de date, in special despre schema acesteia. Vezi de asemenea <link linkend="security.errors">Raportul erorilor</link> si <link linkend="ref.errorfunc">Manipularea Erorilor si a functiilor de logare</link>. </simpara> </listitem> <listitem> <simpara> Se pot folosi proceduri si seturi de functii stocate sau metode predefinite, pentru ca utilizatorii sa nu interactioneze direct cu tabelele, dar aceste metode au alte consecinte. </simpara> </listitem> </itemizedlist> <simpara> In afara de acestea, puteti loga query-urile in interiorul scriptului si in baza de date, daca aceasta suporta acest lucru. Bineinteles, fenomenul de logging nu poate preveni atacurile sau incercarile de a vatama baza de date, dar pot fi folositoare pentru a lua urmele intruziunilor. Acestea se pot dovedi foarte eficiente in gasirea potentialelor gauri din aplicatii. Mai multa informatie despre script este cu atat mai bine venita. </simpara> </sect2> </sect1> </chapter> <!-- Keep this comment at the end of the file Local variables: mode: sgml sgml-omittag:t sgml-shorttag:t sgml-minimize-attributes:nil sgml-always-quote-attributes:t sgml-indent-step:1 sgml-indent-data:t indent-tabs-mode:nil sgml-parent-document:nil sgml-default-dtd-file:"../../manual.ced" sgml-exposed-tags:nil sgml-local-catalogs:nil sgml-local-ecat-files:nil End: vim600: syn=xml fen fdm=syntax fdl=2 si vim: et tw=78 syn=sgml vi: ts=1 sw=1 -->
http://cvs.php.net/co.php/phpdoc-ro/security/database.xml?r=1.1&p=1 Index: phpdoc-ro/security/database.xml +++ phpdoc-ro/security/database.xml <?xml version="1.0" encoding="iso-8859-1"?> <!-- $Revision: 1.1 $ --> <!-- splitted from ./index.xml, last change in rev 1.66 --> <chapter id="security.database"> <title>Securitatea Bazelor de Date</title> <simpara> In zilele noastre, bazele de date sunt componente esentiale ale aplicatiilor web, dand posibilitatea acestora de a servi continut dinamic. Datorita faptului ca informatii secrete sau cu caracter confidential se stocheaza adesea intr-o baza de date, va trebui sa luati in considerare protejarea bazelor de date. </simpara> <simpara> Pentru a primi sau trimite orice informatie, trebuie sa te conectezi la baza de date, sa trimiti un query valid, sa iei rezultatele, si sa inchizi conexiunea. Recent, cel mai folosit limbaj query pentru asemenea tip de interactiune este Nowadays, the commonly used query language in this interaction is the Structured Query Language (SQL). Vezi cum un atacator poate <link linkend="security.database.sql-injection">sa manipuleze un query SQL</link>. </simpara> <simpara> Dupa cum probabil ati presupus, PHP nu va poate proteja bazele de date in sine. Urmatoarele sectiuni tind sa fie o introducere in notiunile de baza ale accesarii si manipularii bazelor de date, cu scripturi PHP. </simpara> <simpara> Pastrati in minte urmatoarea regula: securitate maxima. Cu cat cresteti masurile de protectie asupra bazelor de date, cu atat mai putina este probabilitatea ca un atacator sa reuseasca sa sustraga date importante sau sa abuzeze de ele. Incercati ca structura si designul bazei de date sa fie cat mai eficient, dupa care va puteti pune problemele legate de securitate. </simpara> <sect1 id="security.database.design"> <title>Designul bazelor de date</title> <simpara> Primul pas este intotdeauna crearea bazei de date, numai in cazul in care nu doriti sa folositi alta de pe alt server. Cand o baza de date este creata, ea este desemnata unui anume utilizator, care a executat comanda de creare. In general numai proprietarul (sau superuserul) pot face orice cu obiectele din acea baza de date, iar pentru a lasa si alti utilizatori sa o foloseasca, ei trebuie sa aiba privilegii. </simpara> <simpara> Aplicatiile pe care le construiti nu ar trebui sa se conecteze niciodata la o baza de date cu privilegii de administrator sau superuser, pentru ca acesti utilizatori pot executa orice fel de query, de exemplu, modificarea schemei (stergerea tabelelor) sau stergerea intregului continut. </simpara> <simpara> Poti crea diferiti useri de baze de date pentru fiecare aspect al aplicatiei, cu drepturi limitate, in functie de task-urile pe care le indeplinesc. Se pot da numai privilegiile strict necesare, si evitati ca acelasi user sa interactioneze cu mai multe baze de date. Aceasta inseamna ca daca un intrus va avea privilegii la baza de date, acesta va avea privilegii limitate si nu va putea sa faca decat schimbari cu impact asupra aplicatie dumneavoastra. </simpara> <simpara> Este bine sa nu implementati toate solutiile prin prisma scriptului web, in schimb incercati sa creati o schema de baza de date cat mai competitiva, din care sa extrageti simplu informatia. Daca sistemul evolueaza, noi porturi vor fi folosite pentru a manipula bazele de date, si va trebui sa reimplementati scripturile pentru fiecare baza de date in parte. </simpara> </sect1> <sect1 id="security.database.connection"> <title>Conectarea la baza de date</title> <simpara> Se poate sa doriti sa va conectati la baza de date folosind encriptia SSL pentru a spori siguranta datelor, sau puteti folosi ssh pentru a cripta datele dintre clientii din retea si serverul de baze de date. Daca este folosita una dintre aceste metode atunci monitorizarea traficului si accesul la informatiile sensibile despre bazele de date ar fi foarte dificila pentru un atacator. </simpara> <!--simpara> If your database server has native SSL support, consider using <link linkend="ref.openssl">OpenSSL functions</link> in communication between PHP and database via SSL. </simpara--> </sect1> <sect1 id="security.database.storage"> <title>Model de stocare criptata</title> <simpara> SSL/SSH protejeaza traversarea datelor de la client la server, insa SSL/SSH nu protejeaza datele stocate in baza de date. SSL este un protocol on-the-wire adica are grija numai de informatia ce traverseaza traseul descris mai sus. </simpara> <simpara> Odata ce atacatorul castiga acces la baza de date direct (trecand peste serverul web), informatia stocata poate fi expusa sau abuzata, numai daca ea este protejata de insasi baza de date. Encriptia datelor este o buna masura pentru a diminua acest risc, dar prea putine baze de date oferta acest tip de encriptie. </simpara> <simpara> Cea mai usoara cale de a rezolva problema este de a crea propriul pachet de encriptie, si apoi folosirea lui cu scripturile PHP. PHP poate asigura urmatoarele extensii ca <link linkend="ref.mcrypt">Mcrypt</link> si <link linkend="ref.mhash">Mhash</link>, care acopera o varietate larga de algoritmi de encriptie. Scriptul cripteaza datele inainte de a fi inserate in baza de date, si le deripteaza la extragerea acestora. Verificati documentatia de rigoare pentru a studia mai multe despre modalitatile de operare ale encriptiei. </simpara> <simpara> In cazul datelor care trebuie sa fie confidentiale, a caror expunere nu este necesara in nici un context, procedeul hashing poate fi luat in considerare. Cel mai cunoscut exemplu este stocarea hash-ului MD5 a unei parole in baza de date, in loc de stocarea ei in format plain. Vezi de asemenea si functiile <function>crypt</function> and <function>md5</function>. </simpara> <example> <title>Utilizarea hash-ului pentru parole</title> <programlisting role="php"> <![CDATA[ <?php // stocam hash-ul parolei --> md5($password) $query = sprintf("INSERT INTO users(name,pwd) VALUES('%s','%s');", addslashes($username), md5($password)); $result = pg_exec($connection, $query); // query - verificare daca utilizatorul a introdus parola corecta $query = sprintf("SELECT 1 FROM users WHERE name='%s' AND pwd='%s';", addslashes($username), md5($password)); $result = pg_exec($connection, $query); if (pg_numrows($result) > 0) { echo "Welcome, $username!"; } else { echo "Authentication failed for $username."; } ?> ]]> </programlisting> </example> </sect1> <sect1 id="security.database.sql-injection"> <title>Injectia SQL</title> <simpara> Multi developeri web nu stiu cum pot fi manipulate query-urile SQL, si acorda toata increderea unei asemenea comenzi. Query-urile SQL pot ocoli controalele de acces, in consecinta sa treaca peste metodele de autentificare si verificarile de autorizatie, iar cateodata pot chiar sa faciliteze accesul la comenzile de sistem. </simpara> <simpara> Injectia directa prin comanda SQL este o tehnica in care atacatorul creaza sau modifica comenzile SQL pentru a scoate la iveala datele sensibile, sau pentru a suprascrie o anumita valoare, sau chiar pentru a executa comenzi periculoase la nivel de sistem. Acest lucru este infaptuit de catre aplicatia care preia inputul utilizatorului si combinata cu parametrii statici formeaza un query SQL. Urmatoarele exemple sunt bazate pe greseli italnite la web developeri. </simpara> <para> Datorita lipsei validarii inputului si conectarii la baza de date cu drepturi de superuser, sau a unui user care poate crea la randul lui alti useri, atacatorul poate crea un superuser in baza de date. <example> <title> Impartirea rezultatelor in mai multe pagini ... si crearea de superuser (PostgreSQL and MySQL) </title> <programlisting role="php"> <![CDATA[ <?php $offset = argv[0]; // atentie, nu se face validarea inputului! $query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;"; // cu PostgreSQL $result = pg_exec($conn, $query); // cu MySQL $result = mysql_query($query); ?> ]]> </programlisting> </example> Utilizatorii obisnuiti fac click pe linkurile 'next', 'prev' unde este variabila <varname>$offset</varname> encodata in URL. Scriptul se asteapta ca variabila <varname>$offset</varname> sa fie un numar decimal. Cu toate acestea, ce se intampla daca cineva incerca sa modifice URL-ul si s-ar folosi de <function>urlencode</function> pentru a insera urmatorul URL: <informalexample> <programlisting role="sql"> <![CDATA[ // in cazul PostgreSQL 0; insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd) select 'crack', usesysid, 't','t','crack' from pg_shadow where usename='postgres'; -- // in cazul MySQL 0; UPDATE user SET Password=PASSWORD('crack') WHERE user='root'; FLUSH PRIVILEGES; ]]> </programlisting> </informalexample> Daca se executa, atunci scriptul va schimba parola superuserului root. Observati ca <literal>0;</literal> este pentru a oferi un offset corect query-ului si pentru a-l termina. </para> <note> <para> Exista o tehnica de a forta parserul SQL sa ignore restul queryului scris de developer, cu ajutorul <literal>--</literal> care este simbolul pentru comentariu in SQL. </para> </note> <para> O reala posibilitate de a afla parole este de a manipula rezultatele din paginile de cautare. Singurul lucru de care are nevoie atacatorul este sa vada daca exista variabile in declaratiile SQL care nu sunt protejate corespunzator. Se pot manipula variabilele din formularele care utilizeaza <literal>WHERE, ORDER BY, LIMIT</literal> sau conditiile <literal>OFFSET</literal> din declaratiile <literal>SELECT</literal> Daca baza de date suporta <literal>UNION</literal>, atacatorul poate incerca sa lipeasca un query diferit la cel original pentru a lista parolele dintr-o tabela arbitrara. Folosirea parolelor criptate este pe deplin incurajata. <example> <title> Listarea unor articole ... si a unor parole (orice server de baze de date) </title> <programlisting role="php"> <![CDATA[ <?php $query = "SELECT id, name, inserted, size FROM products WHERE size = '$size' ORDER BY $order LIMIT $limit, $offset;"; $result = odbc_exec($conn, $query); ?> ]]> </programlisting> </example> Partea statica a queryului poate fi combinata cu inca un <literal>SELECT</literal> care sa arate parolele: <informalexample> <programlisting role="sql"> <![CDATA[ ' union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable; -- ]]> </programlisting> </informalexample> In acest query (ne-am jucat cu <literal>'</literal> si <literal>--</literal>) care au fost asignate variabilei <varname>$query</varname>, adica query-ului cu probleme. </para> <para> Comanda SQL UPDATE nu este nici ea ocolita de probleme. Aceste query-uri sunt amenintate de atacurile prin taierea si alipirea unui nou query. In plus, atacatorul se mai poate juca si cu declaratia <literal>SET</literal>. In acest caz, atacatorul trebuie sa cunoasca schema SQL a tabelului din care doreste sa extraga sau sa manipuleze informatia. Acest lucru poate fi facut prin examinarea codului sursa, deci a variabilelor unui formular, sau prin procedeul brute-force. Nu exista multe conventii prin care se delimiteaza campurile pentru user sau parola. <example> <title> Formular de resetare a parolei ... si de castigare a unor privilegii (orice baza de date) </title> <programlisting role="php"> <![CDATA[ <?php $query = "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';"; ?> ]]> </programlisting> </example> Dar un utilizator rau voit introduce <literal>' or uid like'%admin%'; --</literal> in locul <varname>$uid</varname> pentru a schimba parola utilizatorului admin, sau pentru a seta valoarea <varname>$pwd</varname> in <literal>"hehehe', admin='yes', trusted=100 "</literal> (cu spatiu la sfarsit) pentru a castiga mai multe privilegii. Apoi queryul ar arata in felul urmator: <informalexample> <programlisting role="php"> <![CDATA[ <?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....;"; ?> ]]> </programlisting> </informalexample> </para> <para> Un exemplu inspaimantator despre cum poti rula comenzi la nivel de sistem de operare pe unele servere de baze de date. <example> <title>Atacarea sistemului de operare pe care lucreaza baza de date (MSSQL Server)</title> <programlisting role="php"> <![CDATA[ <?php $query = "SELECT * FROM products WHERE id LIKE '%$prod%'"; $result = mssql_query($query); ?> ]]> </programlisting> </example> Daca un atacator introduce valoarea <literal>a%' exec master..xp_cmdshell 'net user test testpass /ADD' --</literal> in loc de <varname>$prod</varname>, atunci variabila <varname>$query</varname> va deveni: <informalexample> <programlisting role="php"> <![CDATA[ <?php $query = "SELECT * FROM products WHERE id LIKE '%a%' exec master..xp_cmdshell 'net user test testpass /ADD'--"; $result = mssql_query($query); ?> ]]> </programlisting> </informalexample> Serverul MSSQL executa declaratia SQL incluzand si comanda de adaugare de user nou in baza de date cu conturi locale. Daca aceasta aplicatie rula ca <literal>sa</literal> si serviciul MSSQLSERVER rula cu destule privilegii, atacatorul avea acum un cont pe serverul respectiv unde sa execute comenzi. </para> <note> <para> Unele dintre exemplele de mai sus sunt legate de animite baze de date. Acest lucru nu inseamna ca atacuri similare nu pot avea loc pe produse similare lor. Baza de date poate fi vulnerabila intr-o maniera asemanatoare, fiind exploatabila prin metode asemanatoare. </para> </note> <sect2 id="security.database.avoiding"> <title>Tehnici de evitare</title> <simpara> Puteti spune ca un atacator trebuie sa detina informatii despre baza de date si schema acesteia in majoritatea exemplelor. In maoritatea cazurilor asa este, dar nu se stie niciodata cum poate fi descoperita aceasta, in mod direct sau indirect. Daca folositi un soft open source, sau alt pachet disponibil publicului larg (content management system sau forum), atacatorii pot duplica cu usurinta codul dumneavoastra. De asemenea un risc il reprezinta si designul necorespunzator al bazei de date. </simpara> <simpara> Aceste atacuri sunt de obicei bazate pe exploatarea codului scris de developeri fara a lua in calcul securitatea lui. Niciodata nu aveti incredere in nici un fel de input, mai ales cand acesta provine de la partea client side, sau camp select, camp ascuns sau cookie.Primul exemplu arata ca asemenea scapari pot duce la query-uri dezastruoase. </simpara> <itemizedlist> <listitem> <simpara> Niciodata nu va conectati la baza de date ca superuser sau ca orice alt utilizator care poate manipula mai multe baze de date decat cea folosita. Folositi intotdeauna useri cu privilegii limitate. </simpara> </listitem> <listitem> <simpara> Verificati daca un input contine tipul de date corect. PHP are o varietate de functii de validare, de la cele mai simple, care pot fi gasite in <link linkend="ref.var">Variable Functions</link> si in <link linkend="ref.ctype">Character Type Functions</link> (e.x. <function>is_numeric</function>, <function>ctype_digit</function>) si pana la <link linkend="ref.pcre">Expresiile regulate compatibile Perl</link>. </simpara> </listitem> <listitem> <para> Daca aplicatia asteapta un input numeric, incercati sa verificati datele cu functia <function>is_numeric</function>, sau schimbati tipul variabilei utilizand functia <function>settype</function>, sau folositi reprezentatia numerica prin <function>sprintf</function>. <example> <title>O metoda mai sigura metoda de query pentru paginare</title> <programlisting role="php"> <![CDATA[ <?php settype($offset, 'integer'); $query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;"; // observati %d din query care inseamna formatul integer, folosirea %s (string) ar fi fara sens $query = sprintf("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;", $offset); ?> ]]> </programlisting> </example> </para> </listitem> <listitem> <simpara> Adauga functia <function>addslashes</function> sau <function>addcslashes</function> fiecarui input care nu este numeric. Vezi <link linkend="security.database.storage">primul exemplu</link>. Dupa cum arata exemplul, eliminarea ghilimelelor in partea statica a query-ului, nu este de ajuns. </simpara> </listitem> <listitem> <simpara> Nu afisati informatii specifice bazei de date, in special despre schema acesteia. Vezi de asemenea <link linkend="security.errors">Raportul erorilor</link> si <link linkend="ref.errorfunc">Manipularea Erorilor si a functiilor de logare</link>. </simpara> </listitem> <listitem> <simpara> Se pot folosi proceduri si seturi de functii stocate sau metode predefinite, pentru ca utilizatorii sa nu interactioneze direct cu tabelele, dar aceste metode au alte consecinte. </simpara> </listitem> </itemizedlist> <simpara> In afara de acestea, puteti loga query-urile in interiorul scriptului si in baza de date, daca aceasta suporta acest lucru. Bineinteles, fenomenul de logging nu poate preveni atacurile sau incercarile de a vatama baza de date, dar pot fi folositoare pentru a lua urmele intruziunilor. Acestea se pot dovedi foarte eficiente in gasirea potentialelor gauri din aplicatii. Mai multa informatie despre script este cu atat mai bine venita. </simpara> </sect2> </sect1> </chapter> <!-- Keep this comment at the end of the file Local variables: mode: sgml sgml-omittag:t sgml-shorttag:t sgml-minimize-attributes:nil sgml-always-quote-attributes:t sgml-indent-step:1 sgml-indent-data:t indent-tabs-mode:nil sgml-parent-document:nil sgml-default-dtd-file:"../../manual.ced" sgml-exposed-tags:nil sgml-local-catalogs:nil sgml-local-ecat-files:nil End: vim600: syn=xml fen fdm=syntax fdl=2 si vim: et tw=78 syn=sgml vi: ts=1 sw=1 -->