The best way has got to be parameterised queries. Then it doesn't matter what the user types in the data goes to the database as a value.
A quick search online shows some possibilities in PHP which is great! Even on this site - http://php.net/manual/en/pdo.prepared-statements.php
which also gives the reasons this is good both for security and performance.
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);
?>
0;
insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
select 'crack', usesysid, 't','t','crack'
from pg_shadow where usename='postgres';
--
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);
?>
' union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable; --
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';";
?>
<?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);
?>
<?php
$query = "SELECT * FROM products
WHERE id LIKE '%a%'
exec master..xp_cmdshell 'net user test testpass /ADD'--";
$result = mssql_query($query);
?>
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(), sqlite_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.
another way to stop sql injection when you odbc_*: create two users,
one has only select permission,
the other has only delete, update, and insert permission,
so you can use select-only user to call odbc_exec while you don't have to check the sql injection; and you use d/u/i only user to update database by calling odbc_prepare and odbc_execute.
Note that PHP 5 introduced filters that you can use for untrusted user input:
http://us.php.net/manual/en/intro.filter.php
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
Another way to prevent SQL injections as opposed to binary, is to use URL Encoding or Hex Encoding.
I haven't seen a complete example of stopping SQL Injections, most refer to use the mysql_real_escape_string function or param statements.
Several examples at http://en.wikipedia.org/wiki/SQL_injection
Which will stop \x00, \n, \r, \, ', " and \x1a based attacks.
Alot depends on your SQL query structure, though vector level attacks are still viable.
Other than that build your own regex replacement to protect specific queries that could alter or compromise your database/results for specific sections of your processing pages.
Also use unique table and field names. Not just putting _ infront of them...
Example, don't store User/s or Customer/s information in a table named the same.
And NEVER use the same form field names for database field names.
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.
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'
Another suggestion would be to build a series of DB procedures / functions that you give the DB user access to to manipulate or select data. That way, all input would run through this exposed interface and all parameters are forced to be typecast (or rejected).
An anonymous comment below suggests using PEAR with prepare() / execute() - though it was posted 3 years ago, it is still true today, though it's even easier now since PDO is included in most distributions. For SET/WHERE clauses and INSERT statements, just prepare the query with question marks in place of the dynamic parameters, bind each value in, then execute it, and it'll do all of the escaping for you, rendering the query immune to injection. Dynamic substitution of ORDER BY or LIMIT clauses has to be done the old fashioned way, though, so you still need to be careful with those.
Even without PDO, if you're using Postgres, you've already got the means to use parameterized queries, and if you're using MySQL, you simply need to ignore the outdated "mysql" extension and use "mysqli" instead.
i just played around with the array_walk function.
It suddenly struck me that almost all super globals are arrays.
So what i discovered was that i can apply the array_walk function to the super globals. Doing so you automatically run a function call through the super globals
With this piece of code i wrote you should be able to secure most of you input data.
<?php
class secure
{
function secureSuperGlobalGET(&$value, $key)
{
$_GET[$key] = htmlspecialchars(stripslashes($_GET[$key]));
$_GET[$key] = str_ireplace("script", "blocked", $_GET[$key]);
$_GET[$key] = mysql_escape_string($_GET[$key]);
return $_GET[$key];
}
function secureSuperGlobalPOST(&$value, $key)
{
$_POST[$key] = htmlspecialchars(stripslashes($_POST[$key]));
$_POST[$key] = str_ireplace("script", "blocked", $_POST[$key]);
$_POST[$key] = mysql_escape_string($_POST[$key]);
return $_POST[$key];
}
function secureGlobals()
{
array_walk($_GET, array($this, 'secureSuperGlobalGET'));
array_walk($_POST, array($this, 'secureSuperGlobalPOST'));
}
}
?>
Note that you can modify this in anyway to suit your needs.
The Script has been tested.
Pangolin is an automatic SQL injection penetration testing tool developed by NOSEC.
Its goal is to detect and take advantage of SQL injection vulnerabilities on web applications. Once it detects one or more SQL injections on the target host, the user can choose among a variety of options to perform an extensive back-end database management system fingerprint, retrieve DBMS session user and database, enumerate users, password hashes, privileges, databases, dump entire or user"s specific DBMS tables/columns, run his own SQL statement, read specific files on the file system and more.
