Mysql
 sql >> Database >  >> RDS >> Mysql

mysql_real_escape_string() e mysql_escape_string() sono sufficienti per la sicurezza dell'app?

@Charles ha ragione!

Ti metti a rischio per diversi tipi di conosciuti Attacchi SQL, inclusi, come hai detto

  • Iniezione SQL:Sì! Mysql_Escape_String probabilmente ANCORA ti tiene suscettibile alle iniezioni SQL, a seconda di dove usi le variabili PHP nelle tue query.

Considera questo:

$sql = "SELECT number FROM PhoneNumbers " .
       "WHERE " . mysql_real_escape_string($field) . " = " . mysql_real_escape_string($value);  

Può essere evitato in modo sicuro e accurato in questo modo? NO! Come mai? perché un hacker potrebbe benissimo farlo ancora:

Ripeti dopo di me:

mysql_real_escape_string() è pensato solo per sfuggire ai dati variabili, NON nomi di tabelle, nomi di colonne e soprattutto campi non LIMIT.

  • exploit LIKE:LIKE "$data%" dove $data potrebbe essere "%" che restituirebbe TUTTI i record ... che può benissimo essere un exploit di sicurezza... immagina solo una ricerca delle ultime quattro cifre di una carta di credito... OOPs! Ora gli hacker possono potenzialmente ricevere ogni numero di carta di credito nel tuo sistema! (A proposito:non è quasi mai consigliabile conservare carte di credito complete!)

  • Exploit di Charset:indipendentemente da ciò che dicono gli hater, Internet Explorer è fermo , nel 2011, vulnerabile agli exploit dei set di personaggi, e questo è se hai progettato correttamente la tua pagina HTML, con l'equivalente di <meta name="charset" value="UTF-8"/> ! Questi attacchi sono MOLTO cattivi in ​​quanto danno all'hacker lo stesso controllo delle iniezioni SQL dirette:ad es. pieno.

Ecco un esempio di codice per dimostrare tutto questo:

// Contains class DBConfig; database information.
require_once('../.dbcreds');                       

$dblink = mysql_connect(DBConfig::$host, DBConfig::$user, DBConfig::$pass);
mysql_select_db(DBConfig::$db);
//print_r($argv);

$sql = sprintf("SELECT url FROM GrabbedURLs WHERE %s LIKE '%s%%' LIMIT %s",
               mysql_real_escape_string($argv[1]),
               mysql_real_escape_string($argv[2]),
               mysql_real_escape_string($argv[3]));
echo "SQL: $sql\n";
$qq = mysql_query($sql);
while (($data = mysql_fetch_array($qq)))
{
        print_r($data);
}

Ecco i risultati di questo codice quando vengono passati vari input:

$ php sql_exploits.php url http://www.reddit.com id
SQL generated: SELECT url FROM GrabbedURLs 
               WHERE url LIKE 'http://www.reddit.com%'
               ORDER BY id;
Returns: Just URLs beginning w/ "http://www.reddit.com"

$ php sql_exploits.php url % id
SQL generated: SELECT url FROM GrabbedURLs 
               WHERE url LIKE '%%' 
               ORDER BY id;
Results: Returns every result Not what you programmed, ergo an exploit --

$ php sql_exploits.php 1=1'http://www.reddit.com ' id Risultati:restituisce ogni colonna e ogni risultato.

Poi ci sono gli exploit LIMIT VERAMENTE brutti:

$ php sql_exploits.php url 
> 'http://www.reddit.com'
> "UNION SELECT name FROM CachedDomains"
Generated SQL: SELECT url FROM GrabbedURLs 
               WHERE url LIKE 'http://reddit.com%' 
               LIMIT 1 
               UNION
               SELECT name FROM CachedDomains;
Returns:  An entirely unexpected, potentially (probably) unauthorized query
          from another, completely different table. 

Che tu capisca o meno l'SQL negli attacchi è irrilevante. Ciò che questo ha dimostrato è che mysql_real_escape_string() è facilmente aggirato anche dal più immaturo degli hacker. Questo perché è un meccanismo di difesa REATTIVO. Risolve solo exploit molto limitati e CONOSCIUTI nel database.

Tutte le evasioni non saranno MAI sufficienti per proteggere i database. In effetti, puoi REAGIRE esplicitamente a ogni exploit CONOSCIUTO e in futuro, molto probabilmente, il tuo codice diventerà vulnerabile agli attacchi scoperti in futuro.

La difesa corretta e unica (veramente) è PROATTIVA:utilizzare dichiarazioni preparate. Le istruzioni preparate sono progettate con particolare attenzione in modo che venga eseguito SOLO SQL valido e PROGRAMMATO. Ciò significa che, se eseguito correttamente, le probabilità che SQL imprevisto possa essere eseguito si riducono drasticamente.

Teoricamente, istruzioni preparate e implementate perfettamente sarebbero impermeabili a TUTTI gli attacchi, noti e sconosciuti, in quanto sono una tecnica SERVER SIDE, gestita dai DATABASE SERVER STESSI e dalle librerie che si interfacciano con il linguaggio di programmazione. Pertanto, hai SEMPRE la garanzia di essere protetto da OGNI HACK CONOSCIUTO, come minimo.

Ed è meno codice:

$pdo = new PDO($dsn);

$column = 'url';
$value = 'http://www.stackoverflow.com/';
$limit = 1;

$validColumns = array('url', 'last_fetched');

// Make sure to validate whether $column is a valid search parameter.
// Default to 'id' if it's an invalid column.
if (!in_array($column, $validColumns) { $column = 'id'; }


$statement = $pdo->prepare('SELECT url FROM GrabbedURLs ' .
                           'WHERE ' . $column . '=? ' .
                           'LIMIT ' . intval($limit));
$statement->execute(array($value));
while (($data = $statement->fetch())) { }

Non è stato così difficile, vero? Ed è il quarantasette percento in meno di codice (195 chars (PDO) vs 375 chars (mysql_). Questo è ciò che chiamo "full of win".

EDIT:Per affrontare tutte le polemiche suscitate da questa risposta, permettetemi di ribadire quanto ho già detto:

L'utilizzo di istruzioni preparate consente di sfruttare le misure di protezione del server SQL stesso, e quindi sei protetto da cose che le persone del server SQL conoscono. Grazie a questo ulteriore livello di protezione, sei molto più sicuro del semplice utilizzo dell'escape, non importa quanto sia completo.